diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 74353173f..acfdd5d52 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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -238,7 +238,7 @@ jobs: gcc_extract_dir: arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -279,7 +279,7 @@ jobs: CXX: g++-${{ matrix.gcc_ver }} steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -312,7 +312,7 @@ jobs: CXX: clang++-${{ matrix.clang_ver }} steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -350,7 +350,7 @@ jobs: LDFLAGS: -m32 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -378,7 +378,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -414,7 +414,7 @@ jobs: CXX: ${{ matrix.CXX }} steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -438,7 +438,7 @@ jobs: image: openthread/environment steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.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 9c1159c69..d1a4a3c4e 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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.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 bc4cf09e1..c8d37141e 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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -91,7 +91,7 @@ jobs: - name: Login to DockerHub if: success() && github.repository == 'openthread/openthread' && github.event_name != 'pull_request' - uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0 + uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index da612bb36..2a5dbc33c 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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.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 1e38d85be..e264d5fd8 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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/otbr.yml b/.github/workflows/otbr.yml index e73387d53..0d4d5c46f 100644 --- a/.github/workflows/otbr.yml +++ b/.github/workflows/otbr.yml @@ -255,7 +255,7 @@ jobs: script/test combine_coverage - name: Upload Coverage continue-on-error: true - uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # v4.3.1 + uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: diff --git a/.github/workflows/otci.yml b/.github/workflows/otci.yml index 87187ad5a..dea78b709 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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.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 8412b8cd9..23ddc0f90 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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -160,7 +160,7 @@ jobs: STRESS_LEVEL: ${{ matrix.stress_level }} steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -208,7 +208,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.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 840ae5ad3..7f3e3cb65 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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -138,7 +138,7 @@ jobs: VIRTUAL_TIME: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -182,7 +182,7 @@ jobs: OT_READLINE: 'readline' steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -232,7 +232,7 @@ jobs: OT_READLINE: 'off' steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -262,7 +262,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -296,7 +296,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -315,7 +315,7 @@ jobs: run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # v4.3.1 + uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: diff --git a/.github/workflows/simulation-1.1.yml b/.github/workflows/simulation-1.1.yml index 2058a9641..f7a8eed31 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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -104,7 +104,7 @@ jobs: VIRTUAL_TIME: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -155,7 +155,7 @@ jobs: MESSAGE_USE_HEAP: ${{ matrix.message_use_heap }} steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -199,7 +199,7 @@ jobs: VIRTUAL_TIME: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -239,7 +239,7 @@ jobs: THREAD_VERSION: 1.1 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -280,7 +280,7 @@ jobs: THREAD_VERSION: 1.1 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -333,7 +333,7 @@ jobs: CXXFLAGS: "-DOPENTHREAD_CONFIG_LOG_PREPEND_UPTIME=0" steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -370,7 +370,7 @@ jobs: COVERAGE: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -404,7 +404,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -423,7 +423,7 @@ jobs: run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # v4.3.1 + uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.2.yml index 809c8fcf1..1941300f7 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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -128,7 +128,7 @@ jobs: INTER_OP_BBR: 0 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -199,7 +199,7 @@ jobs: MULTIPLY: 3 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -250,7 +250,7 @@ jobs: ADDON_FEAT_1_2: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -294,7 +294,7 @@ jobs: VIRTUAL_TIME: 0 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -344,7 +344,7 @@ jobs: INTER_OP: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -405,7 +405,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -424,7 +424,7 @@ jobs: run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # v4.3.1 + uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: diff --git a/.github/workflows/size.yml b/.github/workflows/size.yml index b113c4fe3..5ba9f3d37 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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.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 6d0634276..9b3983af6 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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -123,7 +123,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -164,7 +164,7 @@ jobs: runs-on: macos-14 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -187,7 +187,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -206,7 +206,7 @@ jobs: run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # v4.3.1 + uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index d255c76e0..9a8a66f71 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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -123,7 +123,7 @@ jobs: run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@5ecb98a3c6b747ed38dc09f787459979aebb39be # v4.3.1 + uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index 0bfebd5f4..054c887fa 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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index f1b1eb0cd..c756e9ea7 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -201,6 +201,7 @@ ot_option(OT_DNS_CLIENT OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE "DNS client") ot_option(OT_DNS_CLIENT_OVER_TCP OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE "Enable dns query over tcp") ot_option(OT_DNS_DSO OPENTHREAD_CONFIG_DNS_DSO_ENABLE "DNS Stateful Operations (DSO)") ot_option(OT_DNS_UPSTREAM_QUERY OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE "Allow sending DNS queries to upstream") +ot_option(OT_DNSSD_DISCOVERY_PROXY OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE "DNS-SD discovery proxy") ot_option(OT_DNSSD_SERVER OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE "DNS-SD server") ot_option(OT_DUA OPENTHREAD_CONFIG_DUA_ENABLE "Domain Unicast Address (DUA)") ot_option(OT_ECDSA OPENTHREAD_CONFIG_ECDSA_ENABLE "ECDSA") @@ -327,6 +328,7 @@ ot_string_option(OT_VENDOR_SW_VERSION OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSI set(OT_POWER_SUPPLY_VALUES "BATTERY" "EXTERNAL" "EXTERNAL_STABLE" "EXTERNAL_UNSTABLE") ot_multi_option(OT_POWER_SUPPLY OT_POWER_SUPPLY_VALUES OPENTHREAD_CONFIG_DEVICE_POWER_SUPPLY OT_POWER_SUPPLY_ "set the device power supply config") +ot_int_option(OT_MAC_CSL_REQUEST_AHEAD_US OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US "set time ahead to deliver CSL frame from MAC to SubMac in microseconds") ot_int_option(OT_MLE_MAX_CHILDREN OPENTHREAD_CONFIG_MLE_MAX_CHILDREN "set maximum number of children") ot_int_option(OT_RCP_RESTORATION_MAX_COUNT OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT "set max RCP restoration count") ot_int_option(OT_RCP_TX_WAIT_TIME_SECS OPENTHREAD_SPINEL_CONFIG_RCP_TX_WAIT_TIME_SECS "set RCP TX wait TIME in seconds") diff --git a/examples/apps/cli/main.c b/examples/apps/cli/main.c index d0c6be2dd..6e18b430e 100644 --- a/examples/apps/cli/main.c +++ b/examples/apps/cli/main.c @@ -142,7 +142,7 @@ int main(int argc, char *argv[]) #endif #if OPENTHREAD_CONFIG_PLATFORM_LOG_CRASH_DUMP_ENABLE - otPlatLogCrashDump(); + IgnoreError(otPlatLogCrashDump()); #endif while (!otSysPseudoResetWasRequested()) diff --git a/examples/platforms/simulation/diag.c b/examples/platforms/simulation/diag.c index dcd9f1d9f..8ce6efcfd 100644 --- a/examples/platforms/simulation/diag.c +++ b/examples/platforms/simulation/diag.c @@ -58,6 +58,13 @@ static bool sGpioValue = false; static uint8_t sRawPowerSetting[OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE]; static uint16_t sRawPowerSettingLength = 0; +void otPlatDiagSetOutputCallback(otInstance *aInstance, otPlatDiagOutputCallback aCallback, void *aContext) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aCallback); + OT_UNUSED_VARIABLE(aContext); +} + void otPlatDiagModeSet(bool aMode) { sDiagMode = aMode; } bool otPlatDiagModeGet(void) { return sDiagMode; } diff --git a/examples/platforms/simulation/dnssd.c b/examples/platforms/simulation/dnssd.c index 7967dd487..84fd8a6cf 100644 --- a/examples/platforms/simulation/dnssd.c +++ b/examples/platforms/simulation/dnssd.c @@ -104,4 +104,64 @@ void otPlatDnssdUnregisterKey(otInstance *aInstance, OT_UNUSED_VARIABLE(aCallback); } +void otPlatDnssdStartBrowser(otInstance *aInstance, const otPlatDnssdBrowser *aBrowser) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aBrowser); +} + +void otPlatDnssdStopBrowser(otInstance *aInstance, const otPlatDnssdBrowser *aBrowser) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aBrowser); +} + +void otPlatDnssdStartSrvResolver(otInstance *aInstance, const otPlatDnssdSrvResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + +void otPlatDnssdStopSrvResolver(otInstance *aInstance, const otPlatDnssdSrvResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + +void otPlatDnssdStartTxtResolver(otInstance *aInstance, const otPlatDnssdTxtResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + +void otPlatDnssdStopTxtResolver(otInstance *aInstance, const otPlatDnssdTxtResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + +void otPlatDnssdStartIp6AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + +void otPlatDnssdStopIp6AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + +void otPlatDnssdStartIp4AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + +void otPlatDnssdStopIp4AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + #endif // OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE diff --git a/examples/platforms/simulation/infra_if.c b/examples/platforms/simulation/infra_if.c index ccb5e9be7..e8db5f84f 100644 --- a/examples/platforms/simulation/infra_if.c +++ b/examples/platforms/simulation/infra_if.c @@ -335,4 +335,4 @@ OT_TOOL_WEAK void otPlatInfraIfRecvIcmp6Nd(otInstance *aInstance, exit(1); } -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && !OPENTHREAD_RADIO diff --git a/examples/platforms/simulation/system.c b/examples/platforms/simulation/system.c index 0b0033f8a..772681393 100644 --- a/examples/platforms/simulation/system.c +++ b/examples/platforms/simulation/system.c @@ -215,7 +215,7 @@ void otSysDeinit(void) platformTrelDeinit(); #endif #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE - // platformInfrIfDeinit(); + platformInfraIfDeinit(); #endif platformLoggingDeinit(); } diff --git a/include/openthread/border_routing.h b/include/openthread/border_routing.h index 403c9b600..55af0131a 100644 --- a/include/openthread/border_routing.h +++ b/include/openthread/border_routing.h @@ -102,6 +102,7 @@ typedef struct otBorderRoutingRouterEntry bool mOtherConfigFlag : 1; ///< The router's Other Config flag (`O` flag). bool mStubRouterFlag : 1; ///< The router's Stub Router flag. bool mIsLocalDevice : 1; ///< This router is the local device (this BR). + bool mIsReachable : 1; ///< This router is reachable. } otBorderRoutingRouterEntry; /** diff --git a/include/openthread/diag.h b/include/openthread/diag.h index 217abf6cb..9362ef378 100644 --- a/include/openthread/diag.h +++ b/include/openthread/diag.h @@ -36,6 +36,7 @@ #define OPENTHREAD_DIAG_H_ #include +#include #ifdef __cplusplus extern "C" { @@ -51,39 +52,38 @@ extern "C" { * */ +/* Represents the pointer to callback to output diag messages. */ +typedef otPlatDiagOutputCallback otDiagOutputCallback; + /** - * Processes a factory diagnostics command line. + * Sets the diag output callback. * - * The output of this function (the content written to @p aOutput) MUST terminate with `\0` and the `\0` is within the - * output buffer. + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aCallback A pointer to a function that is called on outputting diag messages. + * @param[in] aContext A pointer to the user context. + * + */ +void otDiagSetOutputCallback(otInstance *aInstance, otDiagOutputCallback aCallback, void *aContext); + +/** + * Processes a factory diagnostics command line. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aArgsLength The number of elements in @p aArgs. * @param[in] aArgs An array of arguments. - * @param[out] aOutput The diagnostics execution result. - * @param[in] aOutputMaxLen The output buffer size. * * @retval OT_ERROR_INVALID_ARGS The command is supported but invalid arguments provided. * @retval OT_ERROR_NONE The command is successfully process. * @retval OT_ERROR_NOT_IMPLEMENTED The command is not supported. * */ -otError otDiagProcessCmd(otInstance *aInstance, - uint8_t aArgsLength, - char *aArgs[], - char *aOutput, - size_t aOutputMaxLen); +otError otDiagProcessCmd(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]); /** * Processes a factory diagnostics command line. * - * The output of this function (the content written to @p aOutput) MUST terminate with `\0` and the `\0` is within the - * output buffer. - * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aString A NULL-terminated input string. - * @param[out] aOutput The diagnostics execution result. - * @param[in] aOutputMaxLen The output buffer size. * * @retval OT_ERROR_NONE The command is successfully process. * @retval OT_ERROR_INVALID_ARGS The command is supported but invalid arguments provided. @@ -91,7 +91,7 @@ otError otDiagProcessCmd(otInstance *aInstance, * @retval OT_ERROR_NO_BUFS The command string is too long. * */ -otError otDiagProcessCmdLine(otInstance *aInstance, const char *aString, char *aOutput, size_t aOutputMaxLen); +otError otDiagProcessCmdLine(otInstance *aInstance, const char *aString); /** * Indicates whether or not the factory diagnostics mode is enabled. diff --git a/include/openthread/instance.h b/include/openthread/instance.h index b6a0cafab..b1079aab7 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 (419) +#define OPENTHREAD_API_VERSION (424) /** * @addtogroup api-instance diff --git a/include/openthread/ip6.h b/include/openthread/ip6.h index 2636702b4..4d1f3ce60 100644 --- a/include/openthread/ip6.h +++ b/include/openthread/ip6.h @@ -381,27 +381,6 @@ otError otIp6UnsubscribeMulticastAddress(otInstance *aInstance, const otIp6Addre */ const otNetifMulticastAddress *otIp6GetMulticastAddresses(otInstance *aInstance); -/** - * Checks if multicast promiscuous mode is enabled on the Thread interface. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * - * @sa otIp6SetMulticastPromiscuousEnabled - * - */ -bool otIp6IsMulticastPromiscuousEnabled(otInstance *aInstance); - -/** - * Enables or disables multicast promiscuous mode on the Thread interface. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[in] aEnabled TRUE to enable Multicast Promiscuous mode, FALSE otherwise. - * - * @sa otIp6IsMulticastPromiscuousEnabled - * - */ -void otIp6SetMulticastPromiscuousEnabled(otInstance *aInstance, bool aEnabled); - /** * Allocate a new message buffer for sending an IPv6 message. * diff --git a/include/openthread/mdns.h b/include/openthread/mdns.h index 56ffff09c..c2a63f0fc 100644 --- a/include/openthread/mdns.h +++ b/include/openthread/mdns.h @@ -500,158 +500,94 @@ otError otMdnsGetNextService(otInstance *aInstance, */ otError otMdnsGetNextKey(otInstance *aInstance, otMdnsIterator *aIterator, otMdnsKey *aKey, otMdnsEntryState *aState); -typedef struct otMdnsBrowseResult otMdnsBrowseResult; -typedef struct otMdnsSrvResult otMdnsSrvResult; -typedef struct otMdnsTxtResult otMdnsTxtResult; -typedef struct otMdnsAddressResult otMdnsAddressResult; - /** - * Represents the callback function used to report a browse result. + * Represents a service browser. * - * @param[in] aInstance The OpenThread instance. - * @param[in] aResult The browse result. + * Refer to `otPlatDnssdBrowser` for documentation of member fields and `otMdnsStartBrowser()` for how they are used. * */ -typedef void (*otMdnsBrowseCallback)(otInstance *aInstance, const otMdnsBrowseResult *aResult); +typedef otPlatDnssdBrowser otMdnsBrowser; /** - * Represents the callback function used to report an SRV resolve result. - * - * @param[in] aInstance The OpenThread instance. - * @param[in] aResult The SRV resolve result. + * Represents the callback function pointer type used to report a browse result. * */ -typedef void (*otMdnsSrvCallback)(otInstance *aInstance, const otMdnsSrvResult *aResult); +typedef otPlatDnssdBrowseCallback otMdnsBrowseCallback; /** - * Represents the callback function used to report a TXT resolve result. - * - * @param[in] aInstance The OpenThread instance. - * @param[in] aResult The TXT resolve result. + * Represents a browse result. * */ -typedef void (*otMdnsTxtCallback)(otInstance *aInstance, const otMdnsTxtResult *aResult); +typedef otPlatDnssdBrowseResult otMdnsBrowseResult; /** - * Represents the callback function use to report a IPv6/IPv4 address resolve result. + * Represents an SRV service resolver. * - * @param[in] aInstance The OpenThread instance. - * @param[in] aResult The address resolve result. + * Refer to `otPlatDnssdSrvResolver` for documentation of member fields and `otMdnsStartSrvResolver()` for how they are + * used. * */ -typedef void (*otMdnsAddressCallback)(otInstance *aInstance, const otMdnsAddressResult *aResult); +typedef otPlatDnssdSrvResolver otMdnsSrvResolver; /** - * Represents a service browser. + * Represents the callback function pointer type used to report an SRV resolve result. * */ -typedef struct otMdnsBrowser -{ - const char *mServiceType; ///< The service type (e.g., "_mt._udp"). MUST NOT include domain name. - const char *mSubTypeLabel; ///< The sub-type label if browsing for sub-type, NULL otherwise. - uint32_t mInfraIfIndex; ///< The infrastructure network interface index. - otMdnsBrowseCallback mCallback; ///< The callback to report result. -} otMdnsBrowser; +typedef otPlatDnssdSrvCallback otMdnsSrvCallback; /** - * Represents a browse result. + * Represents an SRV resolver result. * */ -struct otMdnsBrowseResult -{ - const char *mServiceType; ///< The service type (e.g., "_mt._udp"). - const char *mSubTypeLabel; ///< The sub-type label if browsing for sub-type, NULL otherwise. - const char *mServiceInstance; ///< Service instance label. - uint32_t mTtl; ///< TTL in seconds. Zero TTL indicates that service is removed. - uint32_t mInfraIfIndex; ///< The infrastructure network interface index. -}; +typedef otPlatDnssdSrvResult otMdnsSrvResult; /** - * Represents an SRV service resolver. + * Represents a TXT service resolver. + * + * Refer to `otPlatDnssdTxtResolver` for documentation of member fields and `otMdnsStartTxtResolver()` for how they are + * used. * */ -typedef struct otMdnsSrvResolver -{ - const char *mServiceInstance; ///< The service instance label. - const char *mServiceType; ///< The service type. - uint32_t mInfraIfIndex; ///< The infrastructure network interface index. - otMdnsSrvCallback mCallback; ///< The callback to report result. -} otMdnsSrvResolver; +typedef otPlatDnssdTxtResolver otMdnsTxtResolver; /** - * Represents an SRV resolver result. + * Represents the callback function pointer type used to report a TXT resolve result. * */ -struct otMdnsSrvResult -{ - const char *mServiceInstance; ///< The service instance name label. - const char *mServiceType; ///< The service type. - const char *mHostName; ///< The host name (e.g., "myhost"). Can be NULL when `mTtl` is zero. - uint16_t mPort; ///< The service port number. - uint16_t mPriority; ///< The service priority. - uint16_t mWeight; ///< The service weight. - uint32_t mTtl; ///< The service TTL in seconds. Zero TTL indicates SRV record is removed. - uint32_t mInfraIfIndex; ///< The infrastructure network interface index. -}; +typedef otPlatDnssdTxtCallback otMdnsTxtCallback; /** - * Represents a TXT service resolver. + * Represents a TXT resolver result. * */ -typedef struct otMdnsTxtResolver -{ - const char *mServiceInstance; ///< Service instance label. - const char *mServiceType; ///< Service type. - uint32_t mInfraIfIndex; ///< The infrastructure network interface index. - otMdnsTxtCallback mCallback; -} otMdnsTxtResolver; +typedef otPlatDnssdTxtResult otMdnsTxtResult; /** - * Represents a TXT resolver result. + * Represents an address resolver. + * + * Refer to `otPlatDnssdAddressResolver` for documentation of member fields and `otMdnsStartIp6AddressResolver()` or + * `otMdnsStartIp4AddressResolver()` for how they are used. * */ -struct otMdnsTxtResult -{ - const char *mServiceInstance; ///< The service instance name label. - const char *mServiceType; ///< The service type. - const uint8_t *mTxtData; ///< Encoded TXT data bytes. Can be NULL when `mTtl` is zero. - uint16_t mTxtDataLength; ///< Length of TXT data. - uint32_t mTtl; ///< The TXT data TTL in seconds. Zero TTL indicates record is removed. - uint32_t mInfraIfIndex; ///< The infrastructure network interface index. -}; +typedef otPlatDnssdAddressResolver otMdnsAddressResolver; /** - * Represents an address resolver. + * Represents the callback function pointer type use to report an IPv6/IPv4 address resolve result. * */ -typedef struct otMdnsAddressResolver -{ - const char *mHostName; ///< The host name (e.g., "myhost"). MUST NOT contain domain name. - uint32_t mInfraIfIndex; ///< The infrastructure network interface index. - otMdnsAddressCallback mCallback; ///< The callback to report result. -} otMdnsAddressResolver; +typedef otPlatDnssdAddressCallback otMdnsAddressCallback; /** * Represents a discovered host address and its TTL. * */ -typedef struct otMdnsAddressAndTtl -{ - otIp6Address mAddress; ///< The IPv6 address. For IPv4 address the IPv4-mapped IPv6 address format is used. - uint32_t mTtl; ///< The TTL in seconds. -} otMdnsAddressAndTtl; +typedef otPlatDnssdAddressAndTtl otMdnsAddressAndTtl; /** * Represents address resolver result. * */ -struct otMdnsAddressResult -{ - const char *mHostName; ///< The host name. - const otMdnsAddressAndTtl *mAddresses; ///< Array of host addresses and their TTL. Can be NULL if empty. - uint16_t mAddressesLength; ///< Number of entries in `mAddresses` array. - uint32_t mInfraIfIndex; ///< The infrastructure network interface index. -}; +typedef otPlatDnssdAddressResult otMdnsAddressResult; /** * Starts a service browser. diff --git a/include/openthread/platform/diag.h b/include/openthread/platform/diag.h index 7bc229946..888ef448c 100644 --- a/include/openthread/platform/diag.h +++ b/include/openthread/platform/diag.h @@ -67,27 +67,38 @@ typedef enum } otGpioMode; /** - * Processes a factory diagnostics command line. + * Pointer to callback to output platform diag messages. + * + * @param[in] aFormat The format string. + * @param[in] aArguments The format string arguments. + * @param[out] aContext A pointer to the user context. + * + */ +typedef void (*otPlatDiagOutputCallback)(const char *aFormat, va_list aArguments, void *aContext); + +/** + * Sets the platform diag output callback. * - * The output of this function (the content written to @p aOutput) MUST terminate with `\0` and the `\0` is within the - * output buffer. + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aCallback A pointer to a function that is called on outputting diag messages. + * @param[in] aContext A pointer to the user context. + * + */ +void otPlatDiagSetOutputCallback(otInstance *aInstance, otPlatDiagOutputCallback aCallback, void *aContext); + +/** + * Processes a factory diagnostics command line. * * @param[in] aInstance The OpenThread instance for current request. * @param[in] aArgsLength The number of arguments in @p aArgs. * @param[in] aArgs The arguments of diagnostics command line. - * @param[out] aOutput The diagnostics execution result. - * @param[in] aOutputMaxLen The output buffer size. * * @retval OT_ERROR_INVALID_ARGS The command is supported but invalid arguments provided. * @retval OT_ERROR_NONE The command is successfully process. * @retval OT_ERROR_INVALID_COMMAND The command is not valid or not supported. * */ -otError otPlatDiagProcess(otInstance *aInstance, - uint8_t aArgsLength, - char *aArgs[], - char *aOutput, - size_t aOutputMaxLen); +otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]); /** * Enables/disables the factory diagnostics mode. diff --git a/include/openthread/platform/dnssd.h b/include/openthread/platform/dnssd.h index e7a2a7f0b..894dff561 100644 --- a/include/openthread/platform/dnssd.h +++ b/include/openthread/platform/dnssd.h @@ -65,7 +65,7 @@ extern "C" { */ typedef enum otPlatDnssdState { - OT_PLAT_DNSSD_STOPPED, ///< Stopped and unable to register any service or host. + OT_PLAT_DNSSD_STOPPED, ///< Stopped and unable to register any service or host, or start any browser/resolver. OT_PLAT_DNSSD_READY, ///< Running and ready to register service or host. } otPlatDnssdState; @@ -149,6 +149,9 @@ typedef struct otPlatDnssdKey * The OpenThread stack will call `otPlatDnssdGetState()` (from this callback or later) to get the new state. The * platform MUST therefore ensure that the returned state from `otPlatDnssdGetState()` is updated before calling this. * + * When the platform signals a state change to `OT_PLAT_DNSSD_STOPPED` using this callback, all active browsers and + * resolvers are considered to be stopped, and any previously registered host, service, key entries as removed. + * * @param[in] aInstance The OpenThread instance structure. * */ @@ -415,6 +418,345 @@ void otPlatDnssdUnregisterKey(otInstance *aInstance, otPlatDnssdRequestId aRequestId, otPlatDnssdRegisterCallback aCallback); +//====================================================================================================================== + +/** + * Represents a browse result. + * + */ +typedef struct otPlatDnssdBrowseResult +{ + const char *mServiceType; ///< The service type (e.g., "_mt._udp"). + const char *mSubTypeLabel; ///< The sub-type label if browsing for sub-type, NULL otherwise. + const char *mServiceInstance; ///< Service instance label. + uint32_t mTtl; ///< TTL in seconds. Zero TTL indicates that service is removed. + uint32_t mInfraIfIndex; ///< The infrastructure network interface index. +} otPlatDnssdBrowseResult; + +/** + * Represents the callback function used to report a browse result. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aResult The browse result. + * + */ +typedef void (*otPlatDnssdBrowseCallback)(otInstance *aInstance, const otPlatDnssdBrowseResult *aResult); + +/** + * Represents a service browser. + * + */ +typedef struct otPlatDnssdBrowser +{ + const char *mServiceType; ///< The service type (e.g., "_mt._udp"). MUST NOT include domain name. + const char *mSubTypeLabel; ///< The sub-type label if browsing for sub-type, NULL otherwise. + uint32_t mInfraIfIndex; ///< The infrastructure network interface index. + otPlatDnssdBrowseCallback mCallback; ///< The callback to report result. +} otPlatDnssdBrowser; + +/** + * Represents an SRV resolver result. + * + */ +typedef struct otPlatDnssdSrvResult +{ + const char *mServiceInstance; ///< The service instance name label. + const char *mServiceType; ///< The service type. + const char *mHostName; ///< The host name (e.g., "myhost"). Can be NULL when `mTtl` is zero. + uint16_t mPort; ///< The service port number. + uint16_t mPriority; ///< The service priority. + uint16_t mWeight; ///< The service weight. + uint32_t mTtl; ///< The service TTL in seconds. Zero TTL indicates SRV record is removed. + uint32_t mInfraIfIndex; ///< The infrastructure network interface index. +} otPlatDnssdSrvResult; + +/** + * Represents the callback function used to report an SRV resolve result. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aResult The SRV resolve result. + * + */ +typedef void (*otPlatDnssdSrvCallback)(otInstance *aInstance, const otPlatDnssdSrvResult *aResult); + +/** + * Represents an SRV service resolver. + * + */ +typedef struct otPlatDnssdSrvResolver +{ + const char *mServiceInstance; ///< The service instance label. + const char *mServiceType; ///< The service type. + uint32_t mInfraIfIndex; ///< The infrastructure network interface index. + otPlatDnssdSrvCallback mCallback; ///< The callback to report result. +} otPlatDnssdSrvResolver; + +/** + * Represents a TXT resolver result. + * + */ +typedef struct otPlatDnssdTxtResult +{ + const char *mServiceInstance; ///< The service instance name label. + const char *mServiceType; ///< The service type. + const uint8_t *mTxtData; ///< Encoded TXT data bytes. Can be NULL when `mTtl` is zero. + uint16_t mTxtDataLength; ///< Length of TXT data. + uint32_t mTtl; ///< The TXT data TTL in seconds. Zero TTL indicates record is removed. + uint32_t mInfraIfIndex; ///< The infrastructure network interface index. +} otPlatDnssdTxtResult; + +/** + * Represents the callback function used to report a TXT resolve result. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aResult The TXT resolve result. + * + */ +typedef void (*otPlatDnssdTxtCallback)(otInstance *aInstance, const otPlatDnssdTxtResult *aResult); + +/** + * Represents a TXT service resolver. + * + */ +typedef struct otPlatDnssdTxtResolver +{ + const char *mServiceInstance; ///< Service instance label. + const char *mServiceType; ///< Service type. + uint32_t mInfraIfIndex; ///< The infrastructure network interface index. + otPlatDnssdTxtCallback mCallback; +} otPlatDnssdTxtResolver; + +/** + * Represents a discovered host address and its TTL. + * + */ +typedef struct otPlatDnssdAddressAndTtl +{ + otIp6Address mAddress; ///< The IPv6 address. For IPv4 address the IPv4-mapped IPv6 address format is used. + uint32_t mTtl; ///< The TTL in seconds. +} otPlatDnssdAddressAndTtl; + +/** + * Represents address resolver result. + * + */ +typedef struct otPlatDnssdAddressResult +{ + const char *mHostName; ///< The host name. + const otPlatDnssdAddressAndTtl *mAddresses; ///< Array of host addresses and their TTL. Can be NULL if empty. + uint16_t mAddressesLength; ///< Number of entries in `mAddresses` array. + uint32_t mInfraIfIndex; ///< The infrastructure network interface index. +} otPlatDnssdAddressResult; + +/** + * Represents the callback function use to report a IPv6/IPv4 address resolve result. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aResult The address resolve result. + * + */ +typedef void (*otPlatDnssdAddressCallback)(otInstance *aInstance, const otPlatDnssdAddressResult *aResult); + +/** + * Represents an address resolver. + * + */ +typedef struct otPlatDnssdAddressResolver +{ + const char *mHostName; ///< The host name (e.g., "myhost"). MUST NOT contain domain name. + uint32_t mInfraIfIndex; ///< The infrastructure network interface index. + otPlatDnssdAddressCallback mCallback; ///< The callback to report result. +} otPlatDnssdAddressResolver; + +/** + * Starts a service browser. + * + * Initiates a continuous search for the specified `mServiceType` in @p aBrowser. For sub-type services, + * `mSubTypeLabel` specifies the sub-type, for base services, `mSubTypeLabel` is set to NULL. + * + * Discovered services should be reported through the `mCallback` function in @p aBrowser. Services that have been + * removed are reported with a TTL value of zero. The callback may be invoked immediately with cached information + * (if available) and potentially before this function returns. When cached results are used, the reported TTL value + * should reflect the original TTL from the last received response. + * + * Multiple browsers can be started for the same service, provided they use different callback functions. + * + * The @p aBrowser and all its contained information (strings) are only valid during this call. The platform MUST save + * a copy of the information if it wants to retain the information after returning from this function. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aBrowser The browser to be started. + * + */ +void otPlatDnssdStartBrowser(otInstance *aInstance, const otPlatDnssdBrowser *aBrowser); + +/** + * Stops a service browser. + * + * No action is performed if no matching browser with the same service and callback is currently active. + * + * The @p aBrowser and all its contained information (strings) are only valid during this call. The platform MUST save + * a copy of the information if it wants to retain the information after returning from this function. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aBrowser The browser to stop. + * + */ +void otPlatDnssdStopBrowser(otInstance *aInstance, const otPlatDnssdBrowser *aBrowser); + +/** + * Starts an SRV record resolver. + * + * Initiates a continuous SRV record resolver for the specified service in @p aResolver. + * + * Discovered information should be reported through the `mCallback` function in @p aResolver. When the service is + * removed it is reported with a TTL value of zero. In this case, `mHostName` may be NULL and other result fields (such + * as `mPort`) will be ignored by the OpenThread stack. + * + * The callback may be invoked immediately with cached information (if available) and potentially before this function + * returns. When cached result is used, the reported TTL value should reflect the original TTL from the last received + * response. + * + * Multiple resolvers can be started for the same service, provided they use different callback functions. + * + * The @p aResolver and all its contained information (strings) are only valid during this call. The platform MUST save + * a copy of the information if it wants to retain the information after returning from this function. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aResolver The resolver to be started. + * + */ +void otPlatDnssdStartSrvResolver(otInstance *aInstance, const otPlatDnssdSrvResolver *aResolver); + +/** + * Stops an SRV record resolver. + * + * No action is performed if no matching resolver with the same service and callback is currently active. + * + * The @p aResolver and all its contained information (strings) are only valid during this call. The platform MUST save + * a copy of the information if it wants to retain the information after returning from this function. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aResolver The resolver to stop. + * + */ +void otPlatDnssdStopSrvResolver(otInstance *aInstance, const otPlatDnssdSrvResolver *aResolver); + +/** + * Starts a TXT record resolver. + * + * Initiates a continuous TXT record resolver for the specified service in @p aResolver. + * + * Discovered information should be reported through the `mCallback` function in @p aResolver. When the TXT record is + * removed it is reported with a TTL value of zero. In this case, `mTxtData` may be NULL, and other result fields + * (such as `mTxtDataLength`) will be ignored by the OpenThread stack. + * + * The callback may be invoked immediately with cached information (if available) and potentially before this function + * returns. When cached result is used, the reported TTL value should reflect the original TTL from the last received + * response. + * + * Multiple resolvers can be started for the same service, provided they use different callback functions. + * + * The @p aResolver and all its contained information (strings) are only valid during this call. The platform MUST save + * a copy of the information if it wants to retain the information after returning from this function. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aResolver The resolver to be started. + * + */ +void otPlatDnssdStartTxtResolver(otInstance *aInstance, const otPlatDnssdTxtResolver *aResolver); + +/** + * Stops a TXT record resolver. + * + * No action is performed if no matching resolver with the same service and callback is currently active. + * + * The @p aResolver and all its contained information (strings) are only valid during this call. The platform MUST save + * a copy of the information if it wants to retain the information after returning from this function. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aResolver The resolver to stop. + * + */ +void otPlatDnssdStopTxtResolver(otInstance *aInstance, const otPlatDnssdTxtResolver *aResolver); + +/** + * Starts an IPv6 address resolver. + * + * Initiates a continuous IPv6 address resolver for the specified host name in @p aResolver. + * + * Discovered addresses should be reported through the `mCallback` function in @p aResolver. The callback should be + * invoked whenever addresses are added or removed, providing an updated list. If all addresses are removed, the + * callback should be invoked with an empty list (`mAddressesLength` set to zero). + * + * The callback may be invoked immediately with cached information (if available) and potentially before this function + * returns. When cached result is used, the reported TTL values should reflect the original TTL from the last received + * response. + * + * Multiple resolvers can be started for the same host name, provided they use different callback functions. + * + * The @p aResolver and all its contained information (strings) are only valid during this call. The platform MUST save + * a copy of the information if it wants to retain the information after returning from this function. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aResolver The resolver to be started. + * + */ +void otPlatDnssdStartIp6AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver); + +/** + * Stops an IPv6 address resolver. + * + * No action is performed if no matching resolver with the same host name and callback is currently active. + * + * The @p aResolver and all its contained information (strings) are only valid during this call. The platform MUST save + * a copy of the information if it wants to retain the information after returning from this function. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aResolver The resolver to stop. + * + */ +void otPlatDnssdStopIp6AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver); + +/** + * Starts an IPv4 address resolver. + * + * Initiates a continuous IPv4 address resolver for the specified host name in @p aResolver. + * + * Discovered addresses should be reported through the `mCallback` function in @p aResolver. The IPv4 addresses are + * represented using the IPv4-mapped IPv6 address format in `mAddresses` array. The callback should be invoked + * whenever addresses are added or removed, providing an updated list. If all addresses are removed, the callback + * should be invoked with an empty list (`mAddressesLength` set to zero). + * + * The callback may be invoked immediately with cached information (if available) and potentially before this function + * returns. When cached result is used, the reported TTL values will reflect the original TTL from the last received + * response. + * + * Multiple resolvers can be started for the same host name, provided they use different callback functions. + * + * The @p aResolver and all its contained information (strings) are only valid during this call. The platform MUST save + * a copy of the information if it wants to retain the information after returning from this function. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aResolver The resolver to be started. + * + */ +void otPlatDnssdStartIp4AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver); + +/** + * Stops an IPv4 address resolver. + * + * No action is performed if no matching resolver with the same host name and callback is currently active. + * + * The @p aResolver and all its contained information (strings) are only valid during this call. The platform MUST save + * a copy of the information if it wants to retain the information after returning from this function. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aResolver The resolver to stop. + * + */ +void otPlatDnssdStopIp4AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver); + /** * @} * diff --git a/include/openthread/srp_client.h b/include/openthread/srp_client.h index 71a217c37..27c87f3d0 100644 --- a/include/openthread/srp_client.h +++ b/include/openthread/srp_client.h @@ -201,7 +201,7 @@ typedef void (*otSrpClientAutoStartCallback)(const otSockAddr *aServerSockAddr, * * - The SRP client is started - `otSrpClientStart()` is called. * - Host name is set - `otSrpClientSetHostName()` is called. - * - At least one host IPv6 address is set - `otSrpClientSetHostName()` is called. + * - At least one host IPv6 address is set - `otSrpClientSetHostAddresses()` is called. * - At least one service is added - `otSrpClientAddService()` is called. * * It does not matter in which order these functions are called. When all conditions are met, the SRP client will diff --git a/script/check-scan-build b/script/check-scan-build index 56925d08a..2d855fb16 100755 --- a/script/check-scan-build +++ b/script/check-scan-build @@ -52,6 +52,8 @@ OT_BUILD_OPTIONS=( "-DOT_DHCP6_CLIENT=ON" "-DOT_DHCP6_SERVER=ON" "-DOT_DIAGNOSTIC=ON" + "-DOT_DNSSD_DISCOVERY_PROXY=ON" + "-DOT_DNSSD_SERVER=ON" "-DOT_DNS_CLIENT=ON" "-DOT_DNS_DSO=ON" "-DOT_ECDSA=ON" diff --git a/script/check-size b/script/check-size index c50ade344..7317b6c37 100755 --- a/script/check-size +++ b/script/check-size @@ -105,9 +105,11 @@ build_nrf52840() case "$2" in new) local sha=${OT_SHA_NEW} + local clone_options=("clone") ;; old) local sha=${OT_SHA_OLD} + local clone_options=("clone" "no-depend") ;; *) exit 128 @@ -119,7 +121,7 @@ build_nrf52840() local config_file="../examples/config/${config_name}" mkdir -p "${OT_TMP_DIR}/${folder}" - script/git-tool clone https://github.com/openthread/ot-nrf528xx.git "${OT_TMP_DIR}/${folder}" + script/git-tool "${clone_options[@]}" https://github.com/openthread/ot-nrf528xx.git "${OT_TMP_DIR}/${folder}" rm -rf "${OT_TMP_DIR}/${folder}/openthread/*" # replace openthread submodule with latest commit git archive "${sha}" | tar x -C "${OT_TMP_DIR}/${folder}/openthread" diff --git a/script/git-tool b/script/git-tool index 4bcdae8e4..c6fb695f0 100755 --- a/script/git-tool +++ b/script/git-tool @@ -80,10 +80,15 @@ try_clone() { local dest_dir - dest_dir="$(git clone "$@" 2>&1 | tee | cut -d\' -f2)" - - cd "${dest_dir}" - get_pr_body | apply_dependencies + if [[ $1 == no-depend ]]; then + shift + git clone "$@" 2>&1 + else + dest_dir="$(git clone "$@" 2>&1 | tee | cut -d\' -f2)" + + cd "${dest_dir}" + get_pr_body | apply_dependencies + fi } print_help() diff --git a/script/make-pretty b/script/make-pretty index 84b6204c5..67885d8b0 100755 --- a/script/make-pretty +++ b/script/make-pretty @@ -110,6 +110,7 @@ OT_CLANG_TIDY_BUILD_OPTS=( '-DOT_DNS_CLIENT=ON' '-DOT_DNS_DSO=ON' '-DOT_DNS_UPSTREAM_QUERY=ON' + "-DOT_DNSSD_DISCOVERY_PROXY=ON" '-DOT_DNSSD_SERVER=ON' '-DOT_DUA=ON' '-DOT_MLR=ON' diff --git a/src/cli/README.md b/src/cli/README.md index 272d4063f..deb90c3d7 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -72,7 +72,7 @@ Done - [log](#log-filename-filename) - [mac](#mac-retries-direct) - [macfilter](#macfilter) -- [meshdiag](#meshdiag-topology) +- [meshdiag](#meshdiag-topology-ip6-addrs-children) - [mliid](#mliid-iid) - [mlr](#mlr-reg-ipaddr--timeout) - [mode](#mode) @@ -1767,34 +1767,6 @@ ff32:40:fdde:ad00:beef:0:0:1 Done ``` -### ipmaddr promiscuous - -Get multicast promiscuous mode. - -```bash -> ipmaddr promiscuous -Disabled -Done -``` - -### ipmaddr promiscuous enable - -Enable multicast promiscuous mode. - -```bash -> ipmaddr promiscuous enable -Done -``` - -### ipmaddr promiscuous disable - -Disable multicast promiscuous mode. - -```bash -> ipmaddr promiscuous disable -Done -``` - ### ipmaddr rlatn Get the Realm-Local All Thread Nodes multicast address. @@ -2117,7 +2089,7 @@ Set the log level. Done ``` -### meshdiag topology [ip6-addrs][children] +### meshdiag topology \[ip6-addrs\] \[children\] Discover network topology (list of routers and their connections). @@ -3533,13 +3505,13 @@ Done Module for controlling service registration in Network Data. Each change in service registration must be sent to leader by `netdata register` command before taking effect. -### service add \ \ \ +### service add \ \ [\] Add service to the Network Data. - enterpriseNumber: IANA enterprise number - serviceData: hex-encoded binary service data -- serverData: hex-encoded binary server data +- serverData: hex-encoded binary server data (empty if not provided) ```bash > service add 44970 112233 aabbcc diff --git a/src/cli/README_BR.md b/src/cli/README_BR.md index e6eb264ea..ed704ee32 100644 --- a/src/cli/README_BR.md +++ b/src/cli/README_BR.md @@ -352,9 +352,11 @@ Info per router: - O: Other Config flag - Stub: Stub Router flag (indicates whether the router is a stub router) - Milliseconds since last received message from this router +- Reachability flag: A router is marked as unreachable if it fails to respond to multiple Neighbor Solicitation probes. +- `(this BR)` is appended when the router is the local device itself. ```bash > br routers -ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) ms-since-rx:1505 +ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) ms-since-rx:1505 reachable:yes Done ``` diff --git a/src/cli/README_NETDATA.md b/src/cli/README_NETDATA.md index 940288596..5deeb7762 100644 --- a/src/cli/README_NETDATA.md +++ b/src/cli/README_NETDATA.md @@ -374,6 +374,7 @@ Service entries are listed under `Services` header: - Flags - s: Stable flag - RLOC16 of devices which added the service entry +- Service ID 6LoWPAN Context IDs are listed under `Contexts` header: @@ -400,7 +401,7 @@ Routes: fd00:1234:0:0::/64 s med a000 fd00:4567:0:0::/64 s med 8000 Services: -44970 5d fddead00beef00007bad0069ce45948504d2 s a000 +44970 5d fddead00beef00007bad0069ce45948504d2 s a000 0 Contexts: fd00:dead:beef:cafe::/64 1 c Commissioning: @@ -417,7 +418,7 @@ fd00:dead:beef:cafe::/64 paros med a000 Routes: fd00:1234:0:0::/64 s med a000 Services: -44970 5d fddead00beef00007bad0069ce45948504d2 s a000 +44970 5d fddead00beef00007bad0069ce45948504d2 s a000 0 Done ``` diff --git a/src/cli/README_UDP.md b/src/cli/README_UDP.md index 720aa880f..7c365ee96 100644 --- a/src/cli/README_UDP.md +++ b/src/cli/README_UDP.md @@ -71,7 +71,7 @@ Assigns a name (i.e. IPv6 address and port) to the example socket. - not specified: Thread network interface. - `-u`: unspecified network interface. - `-b`: Backbone network interface. -- ip: the IPv6 address or the unspecified IPv6 address (`::`). +- ip: the unicast IPv6 address or the unspecified IPv6 address (`::`). - port: the UDP port ```bash @@ -83,6 +83,8 @@ Done Done ``` +> Note: to receive datagrams sent to a multicast IPv6 address, the unspecified IPv6 address must be used. Using a multicast address for the \ argument is not supported. Also, the node must subscribe to the multicast group using `ipmaddr add` before it can receive UDP multicast. + ### close Closes the example socket. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index d70978872..7680a4f58 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -169,6 +169,10 @@ Interpreter::Interpreter(Instance *aInstance, otCliOutputCallback aCallback, voi #if (OPENTHREAD_FTD || OPENTHREAD_MTD) && OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK otIp6SetReceiveCallback(GetInstancePtr(), &Interpreter::HandleIp6Receive, this); #endif +#if OPENTHREAD_CONFIG_DIAG_ENABLE + otDiagSetOutputCallback(GetInstancePtr(), &Interpreter::HandleDiagOutput, this); +#endif + ClearAllBytes(mUserCommands); OutputPrompt(); @@ -210,19 +214,20 @@ void Interpreter::OutputResult(otError aError) #if OPENTHREAD_CONFIG_DIAG_ENABLE template <> otError Interpreter::Process(Arg aArgs[]) { - otError error; - char *args[kMaxArgs]; - char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; + char *args[kMaxArgs]; // all diagnostics related features are processed within diagnostics module Arg::CopyArgsToStringArray(aArgs, args); - error = otDiagProcessCmd(GetInstancePtr(), Arg::GetArgsLength(aArgs), args, output, sizeof(output)); - - OutputFormat("%s", output); + return otDiagProcessCmd(GetInstancePtr(), Arg::GetArgsLength(aArgs), args); +} - return error; +void Interpreter::HandleDiagOutput(const char *aFormat, va_list aArguments, void *aContext) +{ + static_cast(aContext)->HandleDiagOutput(aFormat, aArguments); } + +void Interpreter::HandleDiagOutput(const char *aFormat, va_list aArguments) { OutputFormatV(aFormat, aArguments); } #endif template <> otError Interpreter::Process(Arg aArgs[]) @@ -3365,35 +3370,6 @@ template <> otError Interpreter::Process(Arg aArgs[]) SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address)); error = otIp6UnsubscribeMulticastAddress(GetInstancePtr(), &address); } - /** - * @cli ipmaddr promiscuous - * @code - * ipmaddr promiscuous - * Disabled - * Done - * @endcode - * @par api_copy - * #otIp6IsMulticastPromiscuousEnabled - */ - else if (aArgs[0] == "promiscuous") - { - /** - * @cli ipmaddr promiscuous (enable,disable) - * @code - * ipmaddr promiscuous enable - * Done - * @endcode - * @code - * ipmaddr promiscuous disable - * Done - * @endcode - * @cparam ipmaddr promiscuous @ca{enable|disable} - * @par api_copy - * #otIp6SetMulticastPromiscuousEnabled - */ - error = - ProcessEnableDisable(aArgs + 1, otIp6IsMulticastPromiscuousEnabled, otIp6SetMulticastPromiscuousEnabled); - } /** * @cli ipmaddr llatn * @code @@ -4596,13 +4572,13 @@ template <> otError Interpreter::Process(Arg aArgs[]) * netdata register * Done * @endcode - * @cparam service add @ca{enterpriseNumber} @ca{serviceData} @ca{serverData} + * @cparam service add @ca{enterpriseNumber} @ca{serviceData} [@ca{serverData}] * @par * Adds service to the network data. * @par * - enterpriseNumber: IANA enterprise number * - serviceData: Hex-encoded binary service data - * - serverData: Hex-encoded binary server data + * - serverData: Hex-encoded binary server data (empty if not provided) * @par * Note: For each change in service registration to take effect, run * the `netdata register` command after running the `service add` command to notify the leader. @@ -4611,10 +4587,17 @@ template <> otError Interpreter::Process(Arg aArgs[]) */ if (aArgs[0] == "add") { - length = sizeof(cfg.mServerConfig.mServerData); - SuccessOrExit(error = aArgs[3].ParseAsHexString(length, cfg.mServerConfig.mServerData)); - VerifyOrExit(length > 0, error = OT_ERROR_INVALID_ARGS); - cfg.mServerConfig.mServerDataLength = static_cast(length); + if (!aArgs[3].IsEmpty()) + { + length = sizeof(cfg.mServerConfig.mServerData); + SuccessOrExit(error = aArgs[3].ParseAsHexString(length, cfg.mServerConfig.mServerData)); + VerifyOrExit(length > 0, error = OT_ERROR_INVALID_ARGS); + cfg.mServerConfig.mServerDataLength = static_cast(length); + } + else + { + cfg.mServerConfig.mServerDataLength = 0; + } cfg.mServerConfig.mStable = true; diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index d5fe30ad9..65af2e839 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -339,6 +339,11 @@ class Interpreter : public OutputImplementer, public Utils #endif // OPENTHREAD_FTD || OPENTHREAD_MTD +#if OPENTHREAD_CONFIG_DIAG_ENABLE + static void HandleDiagOutput(const char *aFormat, va_list aArguments, void *aContext); + void HandleDiagOutput(const char *aFormat, va_list aArguments); +#endif + void SetCommandTimeout(uint32_t aTimeoutMilli); static void HandleTimer(Timer &aTimer); diff --git a/src/cli/cli_br.cpp b/src/cli/cli_br.cpp index b76e76716..cee063d01 100644 --- a/src/cli/cli_br.cpp +++ b/src/cli/cli_br.cpp @@ -518,7 +518,7 @@ template <> otError Br::Process(Arg aArgs[]) * @cli br routers * @code * br routers - * ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) ms-since-rx:1505 + * ff02:0:0:0:0:0:0:1 (M:0 O:0 Stub:1) ms-since-rx:1505 reachable:yes * Done * @endcode * @par @@ -530,6 +530,9 @@ template <> otError Br::Process(Arg aArgs[]) * - O: Other Config flag * - Stub: Stub Router flag (indicates whether the router is a stub router) * - Milliseconds since last received message from this router + * - Reachability flag: A router is marked as unreachable if it fails to respond to multiple Neighbor Solicitation + * probes. + * - `(this BR)` is appended when the router is the local device itself. * @sa otBorderRoutingGetNextRouterEntry */ template <> otError Br::Process(Arg aArgs[]) @@ -559,7 +562,8 @@ void Br::OutputRouterInfo(const otBorderRoutingRouterEntry &aEntry, RouterOutput if (aMode == kLongVersion) { - OutputFormat(" ms-since-rx:%lu", ToUlong(aEntry.mMsecSinceLastUpdate)); + OutputFormat(" ms-since-rx:%lu reachable:%s", ToUlong(aEntry.mMsecSinceLastUpdate), + aEntry.mIsReachable ? "yes" : "no"); if (aEntry.mIsLocalDevice) { diff --git a/src/cli/cli_network_data.cpp b/src/cli/cli_network_data.cpp index 5d1d769c0..629b59f20 100644 --- a/src/cli/cli_network_data.cpp +++ b/src/cli/cli_network_data.cpp @@ -170,7 +170,7 @@ void NetworkData::OutputService(const otServiceConfig &aConfig) OutputFormat(" s"); } - OutputLine(" %04x", aConfig.mServerConfig.mRloc16); + OutputLine(" %04x %u", aConfig.mServerConfig.mRloc16, aConfig.mServiceId); } /** @@ -713,8 +713,8 @@ otError NetworkData::OutputBinary(bool aLocal) * Routes: * fd49:7770:7fc5:0::/64 s med 4000 * Services: - * 44970 5d c000 s 4000 - * 44970 01 9a04b000000e10 s 4000 + * 44970 5d c000 s 4000 0 + * 44970 01 9a04b000000e10 s 4000 1 * Contexts: * fd00:dead:beef:cafe::/64 1 c * Commissioning: @@ -771,6 +771,7 @@ otError NetworkData::OutputBinary(bool aLocal) * * Flags * * s: Stable flag * * RLOC16 of devices which added the service entry + * * Service ID * @par * 6LoWPAN Context IDs are listed under `Contexts` header: * * The prefix diff --git a/src/cli/cli_udp.cpp b/src/cli/cli_udp.cpp index fba14f1c0..257b188ab 100644 --- a/src/cli/cli_udp.cpp +++ b/src/cli/cli_udp.cpp @@ -70,9 +70,9 @@ UdpExample::UdpExample(otInstance *aInstance, OutputImplementer &aOutputImplemen * - `-u`: Unspecified network interface, which means that the UDP/IPv6 stack determines which * network interface to bind the socket to. * - `-b`: Backbone network interface is used. - * - `ip`: IPv6 address to bind to. If you wish to have the UDP/IPv6 stack assign the binding - * IPv6 address, then you can use the following value to use the unspecified - * IPv6 address: `::`. Each example uses the unspecified IPv6 address. + * - `ip`: Unicast IPv6 address to bind to. If you wish to have the UDP/IPv6 stack assign the binding + * IPv6 address, or if you wish to bind to multicast IPv6 addresses, then you can use the following + * value to use the unspecified IPv6 address: `::`. Each example uses the unspecified IPv6 address. * - `port`: UDP port number to bind to. Each of the examples is using port number 1234. * @par * Assigns an IPv6 address and a port to an open socket, which binds the socket for communication. diff --git a/src/core/api/diags_api.cpp b/src/core/api/diags_api.cpp index a87bde581..cb8488ddc 100644 --- a/src/core/api/diags_api.cpp +++ b/src/core/api/diags_api.cpp @@ -42,18 +42,22 @@ using namespace ot; -otError otDiagProcessCmdLine(otInstance *aInstance, const char *aString, char *aOutput, size_t aOutputMaxLen) +otError otDiagProcessCmdLine(otInstance *aInstance, const char *aString) { AssertPointerIsNotNull(aString); - return AsCoreType(aInstance).Get().ProcessLine(aString, aOutput, aOutputMaxLen); + return AsCoreType(aInstance).Get().ProcessLine(aString); } -otError otDiagProcessCmd(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +otError otDiagProcessCmd(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) { - return AsCoreType(aInstance).Get().ProcessCmd(aArgsLength, aArgs, aOutput, aOutputMaxLen); + return AsCoreType(aInstance).Get().ProcessCmd(aArgsLength, aArgs); } bool otDiagIsEnabled(otInstance *aInstance) { return AsCoreType(aInstance).Get().IsEnabled(); } +void otDiagSetOutputCallback(otInstance *aInstance, otDiagOutputCallback aCallback, void *aContext) +{ + AsCoreType(aInstance).Get().SetOutputCallback(aCallback, aContext); +} #endif // OPENTHREAD_CONFIG_DIAG_ENABLE diff --git a/src/core/api/ip6_api.cpp b/src/core/api/ip6_api.cpp index 5ca4f50d9..29fee9da3 100644 --- a/src/core/api/ip6_api.cpp +++ b/src/core/api/ip6_api.cpp @@ -105,16 +105,6 @@ otError otIp6UnsubscribeMulticastAddress(otInstance *aInstance, const otIp6Addre return AsCoreType(aInstance).Get().UnsubscribeExternalMulticast(AsCoreType(aAddress)); } -bool otIp6IsMulticastPromiscuousEnabled(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().IsMulticastPromiscuousEnabled(); -} - -void otIp6SetMulticastPromiscuousEnabled(otInstance *aInstance, bool aEnabled) -{ - AsCoreType(aInstance).Get().SetMulticastPromiscuous(aEnabled); -} - void otIp6SetReceiveCallback(otInstance *aInstance, otIp6ReceiveCallback aCallback, void *aCallbackContext) { AsCoreType(aInstance).Get().SetReceiveDatagramCallback(aCallback, aCallbackContext); diff --git a/src/core/api/thread_api.cpp b/src/core/api/thread_api.cpp index 97e5508f0..eb55386f6 100644 --- a/src/core/api/thread_api.cpp +++ b/src/core/api/thread_api.cpp @@ -79,7 +79,13 @@ otError otThreadSetExtendedPanId(otInstance *aInstance, const otExtendedPanId *a otError otThreadGetLeaderRloc(otInstance *aInstance, otIp6Address *aLeaderRloc) { - return AsCoreType(aInstance).Get().GetLeaderAddress(AsCoreType(aLeaderRloc)); + Error error = kErrorNone; + + VerifyOrExit(AsCoreType(aInstance).Get().GetRloc16() != Mle::kInvalidRloc16, error = kErrorDetached); + AsCoreType(aInstance).Get().GetLeaderRloc(AsCoreType(aLeaderRloc)); + +exit: + return error; } otLinkModeConfig otThreadGetLinkMode(otInstance *aInstance) @@ -145,12 +151,12 @@ otError otThreadSetNetworkKeyRef(otInstance *aInstance, otNetworkKeyRef aKeyRef) const otIp6Address *otThreadGetRloc(otInstance *aInstance) { - return &AsCoreType(aInstance).Get().GetMeshLocal16(); + return &AsCoreType(aInstance).Get().GetMeshLocalRloc(); } const otIp6Address *otThreadGetMeshLocalEid(otInstance *aInstance) { - return &AsCoreType(aInstance).Get().GetMeshLocal64(); + return &AsCoreType(aInstance).Get().GetMeshLocalEid(); } const otMeshLocalPrefix *otThreadGetMeshLocalPrefix(otInstance *aInstance) @@ -189,7 +195,13 @@ const otIp6Address *otThreadGetRealmLocalAllThreadNodesMulticastAddress(otInstan otError otThreadGetServiceAloc(otInstance *aInstance, uint8_t aServiceId, otIp6Address *aServiceAloc) { - return AsCoreType(aInstance).Get().GetServiceAloc(aServiceId, AsCoreType(aServiceAloc)); + Error error = kErrorNone; + + VerifyOrExit(AsCoreType(aInstance).Get().GetRloc16() != Mle::kInvalidRloc16, error = kErrorDetached); + AsCoreType(aInstance).Get().GetServiceAloc(aServiceId, AsCoreType(aServiceAloc)); + +exit: + return error; } const char *otThreadGetNetworkName(otInstance *aInstance) @@ -275,7 +287,8 @@ uint32_t otThreadGetKeySequenceCounter(otInstance *aInstance) void otThreadSetKeySequenceCounter(otInstance *aInstance, uint32_t aKeySequenceCounter) { - AsCoreType(aInstance).Get().SetCurrentKeySequence(aKeySequenceCounter, KeyManager::kForceUpdate); + AsCoreType(aInstance).Get().SetCurrentKeySequence( + aKeySequenceCounter, KeyManager::kForceUpdate | KeyManager::kGuardTimerUnchanged); } uint16_t otThreadGetKeySwitchGuardTime(otInstance *aInstance) diff --git a/src/core/api/thread_ftd_api.cpp b/src/core/api/thread_ftd_api.cpp index ec3b9b4d4..6b3f554ee 100644 --- a/src/core/api/thread_ftd_api.cpp +++ b/src/core/api/thread_ftd_api.cpp @@ -250,15 +250,7 @@ otError otThreadGetChildNextIp6Address(otInstance *aInstance, VerifyOrExit(child != nullptr, error = kErrorInvalidArgs); VerifyOrExit(child->IsStateValidOrRestoring(), error = kErrorInvalidArgs); - { - Child::AddressIterator iter(*child, *aIterator); - - VerifyOrExit(!iter.IsDone(), error = kErrorNotFound); - *aAddress = *iter.GetAddress(); - - iter++; - *aIterator = iter.GetAsIndex(); - } + error = child->GetNextIp6Address(*aIterator, AsCoreType(aAddress)); exit: return error; diff --git a/src/core/api/udp_api.cpp b/src/core/api/udp_api.cpp index 5141a50b6..01c5a3408 100644 --- a/src/core/api/udp_api.cpp +++ b/src/core/api/udp_api.cpp @@ -98,7 +98,7 @@ void otUdpForwardReceive(otInstance *aInstance, { Ip6::MessageInfo messageInfo; - messageInfo.SetSockAddr(AsCoreType(aInstance).Get().GetMeshLocal16()); + messageInfo.SetSockAddr(AsCoreType(aInstance).Get().GetMeshLocalRloc()); messageInfo.SetSockPort(aSockPort); messageInfo.SetPeerAddr(AsCoreType(aPeerAddr)); messageInfo.SetPeerPort(aPeerPort); diff --git a/src/core/backbone_router/backbone_tmf.cpp b/src/core/backbone_router/backbone_tmf.cpp index d49e31c62..63abcf2e0 100644 --- a/src/core/backbone_router/backbone_tmf.cpp +++ b/src/core/backbone_router/backbone_tmf.cpp @@ -120,8 +120,8 @@ bool BackboneTmfAgent::IsBackboneTmfMessage(const Ip6::MessageInfo &aMessageInfo // 2. All Domain BBRs (Link-Local scope) // 3. A Backbone Link-Local address // The source must be a Backbone Link-local address. - return (Get().IsEnabled() && src.IsLinkLocal() && - (dst.IsLinkLocal() || dst == Get().GetAllNetworkBackboneRoutersAddress() || + return (Get().IsEnabled() && src.IsLinkLocalUnicast() && + (dst.IsLinkLocalUnicast() || dst == Get().GetAllNetworkBackboneRoutersAddress() || dst == Get().GetAllDomainBackboneRoutersAddress())); } diff --git a/src/core/backbone_router/bbr_leader.cpp b/src/core/backbone_router/bbr_leader.cpp index 5b605ac5e..85cb08be1 100644 --- a/src/core/backbone_router/bbr_leader.cpp +++ b/src/core/backbone_router/bbr_leader.cpp @@ -52,7 +52,7 @@ Leader::Leader(Instance &aInstance) void Leader::Reset(void) { // Invalid server short address indicates no available Backbone Router service in the Thread Network. - mConfig.mServer16 = Mac::kShortAddrInvalid; + mConfig.mServer16 = Mle::kInvalidRloc16; // Domain Prefix Length 0 indicates no available Domain Prefix in the Thread network. mDomainPrefix.SetLength(0); @@ -152,11 +152,11 @@ void Leader::UpdateBackboneRouterPrimary(void) if (config.mServer16 != mConfig.mServer16) { - if (config.mServer16 == Mac::kShortAddrInvalid) + if (config.mServer16 == Mle::kInvalidRloc16) { state = kStateRemoved; } - else if (mConfig.mServer16 == Mac::kShortAddrInvalid) + else if (mConfig.mServer16 == Mle::kInvalidRloc16) { state = kStateAdded; } @@ -166,7 +166,7 @@ void Leader::UpdateBackboneRouterPrimary(void) state = kStateToTriggerRereg; } } - else if (config.mServer16 == Mac::kShortAddrInvalid) + else if (config.mServer16 == Mle::kInvalidRloc16) { // If no Primary all the time. state = kStateNone; @@ -185,7 +185,7 @@ void Leader::UpdateBackboneRouterPrimary(void) } // Restrain the range of MLR timeout to be always valid - if (config.mServer16 != Mac::kShortAddrInvalid) + if (config.mServer16 != Mle::kInvalidRloc16) { uint32_t origTimeout = config.mMlrTimeout; diff --git a/src/core/backbone_router/bbr_leader.hpp b/src/core/backbone_router/bbr_leader.hpp index 4102651f2..2822bd8de 100644 --- a/src/core/backbone_router/bbr_leader.hpp +++ b/src/core/backbone_router/bbr_leader.hpp @@ -143,7 +143,7 @@ class Leader : public InstanceLocator, private NonCopyable /** * Gets the short address of the Primary Backbone Router. * - * @returns short address of Primary Backbone Router, or Mac::kShortAddrInvalid if no Primary Backbone Router. + * @returns short address of Primary Backbone Router, or Mle::kInvalidRloc16 if no Primary Backbone Router. * */ uint16_t GetServer16(void) const { return mConfig.mServer16; } @@ -155,7 +155,7 @@ class Leader : public InstanceLocator, private NonCopyable * @retval FALSE If there is no Primary Backbone Router. * */ - bool HasPrimary(void) const { return mConfig.mServer16 != Mac::kShortAddrInvalid; } + bool HasPrimary(void) const { return mConfig.mServer16 != Mle::kInvalidRloc16; } /** * Gets the Domain Prefix in the Thread Network. diff --git a/src/core/backbone_router/bbr_local.cpp b/src/core/backbone_router/bbr_local.cpp index 66cb8f519..2b4ab5f02 100644 --- a/src/core/backbone_router/bbr_local.cpp +++ b/src/core/backbone_router/bbr_local.cpp @@ -254,7 +254,7 @@ void Local::HandleBackboneRouterPrimaryUpdate(Leader::State aState, const Config VerifyOrExit(IsEnabled() && Get().IsAttached()); // Wait some jitter before trying to Register. - if (aConfig.mServer16 == Mac::kShortAddrInvalid) + if (aConfig.mServer16 == Mle::kInvalidRloc16) { mRegistrationTimeout = 1; diff --git a/src/core/backbone_router/bbr_manager.cpp b/src/core/backbone_router/bbr_manager.cpp index 4fb15e4bc..7a90d1c71 100644 --- a/src/core/backbone_router/bbr_manager.cpp +++ b/src/core/backbone_router/bbr_manager.cpp @@ -527,7 +527,7 @@ Error Manager::SendBackboneQuery(const Ip6::Address &aDua, uint16_t aRloc16) SuccessOrExit(error = Tlv::Append(*message, aDua)); - if (aRloc16 != Mac::kShortAddrInvalid) + if (aRloc16 != Mle::kInvalidRloc16) { SuccessOrExit(error = Tlv::Append(*message, aRloc16)); } @@ -550,7 +550,7 @@ template <> void Manager::HandleTmf(Coap::Message &aMessage, { Error error = kErrorNone; Ip6::Address dua; - uint16_t rloc16 = Mac::kShortAddrInvalid; + uint16_t rloc16 = Mle::kInvalidRloc16; NdProxyTable::NdProxy *ndProxy; VerifyOrExit(aMessageInfo.IsHostInterface(), error = kErrorDrop); @@ -583,7 +583,7 @@ template <> void Manager::HandleTmf(Coap::Message &aMessage, Ip6::InterfaceIdentifier meshLocalIid; uint16_t networkNameOffset, networkNameLength; uint32_t timeSinceLastTransaction; - uint16_t srcRloc16 = Mac::kShortAddrInvalid; + uint16_t srcRloc16 = Mle::kInvalidRloc16; VerifyOrExit(aMessageInfo.IsHostInterface(), error = kErrorDrop); @@ -606,7 +606,7 @@ template <> void Manager::HandleTmf(Coap::Message &aMessage, { HandleProactiveBackboneNotification(dua, meshLocalIid, timeSinceLastTransaction); } - else if (srcRloc16 == Mac::kShortAddrInvalid) + else if (srcRloc16 == Mle::kInvalidRloc16) { HandleDadBackboneAnswer(dua, meshLocalIid); } @@ -626,7 +626,7 @@ Error Manager::SendProactiveBackboneNotification(const Ip6::Address uint32_t aTimeSinceLastTransaction) { return SendBackboneAnswer(Get().GetAllDomainBackboneRoutersAddress(), aDua, aMeshLocalIid, - aTimeSinceLastTransaction, Mac::kShortAddrInvalid); + aTimeSinceLastTransaction, Mle::kInvalidRloc16); } Error Manager::SendBackboneAnswer(const Ip6::MessageInfo &aQueryMessageInfo, @@ -664,7 +664,7 @@ Error Manager::SendBackboneAnswer(const Ip6::Address &aDstAddr, SuccessOrExit(error = Tlv::Append( *message, Get().GetNetworkName().GetAsCString())); - if (aSrcRloc16 != Mac::kShortAddrInvalid) + if (aSrcRloc16 != Mle::kInvalidRloc16) { SuccessOrExit(Tlv::Append(*message, aSrcRloc16)); } diff --git a/src/core/backbone_router/bbr_manager.hpp b/src/core/backbone_router/bbr_manager.hpp index 87d8021bc..6a6d19d12 100644 --- a/src/core/backbone_router/bbr_manager.hpp +++ b/src/core/backbone_router/bbr_manager.hpp @@ -146,7 +146,7 @@ class Manager : public InstanceLocator, private NonCopyable * Sends BB.qry on the Backbone link. * * @param[in] aDua The Domain Unicast Address to query. - * @param[in] aRloc16 The short address of the address resolution initiator or `Mac::kShortAddrInvalid` for + * @param[in] aRloc16 The short address of the address resolution initiator or `Mle::kInvalidRloc16` for * DUA DAD. * * @retval kErrorNone Successfully sent BB.qry on backbone link. @@ -154,7 +154,7 @@ class Manager : public InstanceLocator, private NonCopyable * @retval kErrorNoBufs If insufficient message buffers available. * */ - Error SendBackboneQuery(const Ip6::Address &aDua, uint16_t aRloc16 = Mac::kShortAddrInvalid); + Error SendBackboneQuery(const Ip6::Address &aDua, uint16_t aRloc16 = Mle::kInvalidRloc16); /** * Send a Proactive Backbone Notification (PRO_BB.ntf) on the Backbone link. diff --git a/src/core/border_router/infra_if.cpp b/src/core/border_router/infra_if.cpp index 0b44ee24f..25efee5b8 100644 --- a/src/core/border_router/infra_if.cpp +++ b/src/core/border_router/infra_if.cpp @@ -156,6 +156,10 @@ Error InfraIf::HandleStateChanged(uint32_t aIfIndex, bool aIsRunning) Get().HandleInfraIfStateChanged(); #endif +#if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE && OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + Get().HandleInfraIfStateChanged(); +#endif + #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE && OPENTHREAD_CONFIG_MULTICAST_DNS_AUTO_ENABLE_ON_INFRA_IF Get().HandleInfraIfStateChanged(); #endif diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index d1ab03028..07b2c6379 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -594,7 +594,7 @@ void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode) header.SetToDefault(); } - mRxRaTracker.DetermineAndSetFlags(header); + mRxRaTracker.SetHeaderFlagsOn(header); SuccessOrExit(error = raMsg.AppendHeader(header)); @@ -980,12 +980,11 @@ void RoutingManager::RxRaTracker::Stop(void) { mRouters.Free(); mLocalRaHeader.Clear(); + mDecisionFactors.Clear(); mExpirationTimer.Stop(); mStaleTimer.Stop(); mRouterTimer.Stop(); - - SignalTableChanged(); } void RoutingManager::RxRaTracker::ProcessRouterAdvertMessage(const RouterAdvert::RxMessage &aRaMessage, @@ -1048,9 +1047,9 @@ void RoutingManager::RxRaTracker::ProcessRouterAdvertMessage(const RouterAdvert: router->mIsLocalDevice = (aRaOrigin == kThisBrOtherEntity); - UpdateRouterOnRx(*router); + router->ResetReachabilityState(); - RemoveRoutersWithNoEntriesOrFlags(); + Evaluate(); exit: return; @@ -1070,13 +1069,11 @@ void RoutingManager::RxRaTracker::ProcessRaHeader(const RouterAdvert::Header &aR if (aRouter.mManagedAddressConfigFlag != managedFlag) { aRouter.mManagedAddressConfigFlag = managedFlag; - SignalTableChanged(); } if (aRouter.mOtherConfigFlag != otherFlag) { aRouter.mOtherConfigFlag = otherFlag; - SignalTableChanged(); } if (aRaOrigin == kThisBrOtherEntity) @@ -1104,7 +1101,6 @@ void RoutingManager::RxRaTracker::ProcessRaHeader(const RouterAdvert::Header &aR if (mLocalRaHeader != oldHeader) { - SignalTableChanged(); Get().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay); } } @@ -1134,10 +1130,6 @@ void RoutingManager::RxRaTracker::ProcessRaHeader(const RouterAdvert::Header &aR entry->SetFrom(aRaHeader); } - mExpirationTimer.FireAtIfEarlier(entry->GetExpireTime()); - - SignalTableChanged(); - exit: return; } @@ -1185,10 +1177,6 @@ void RoutingManager::RxRaTracker::ProcessPrefixInfoOption(const PrefixInfoOption entry->AdoptValidAndPreferredLifetimesFrom(newPrefix); } - mExpirationTimer.FireAtIfEarlier(entry->GetExpireTime()); - - SignalTableChanged(); - exit: return; } @@ -1241,10 +1229,6 @@ void RoutingManager::RxRaTracker::ProcessRouteInfoOption(const RouteInfoOption & entry->SetFrom(aRio); } - mExpirationTimer.FireAtIfEarlier(entry->GetExpireTime()); - - SignalTableChanged(); - exit: return; } @@ -1311,73 +1295,6 @@ template void RoutingManager::RxRaTracker::Entry: #endif // !OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE -bool RoutingManager::RxRaTracker::ContainsDefaultOrNonUlaRoutePrefix(void) const -{ - bool contains = false; - - for (const Router &router : mRouters) - { - if (router.mRoutePrefixes.ContainsMatching(RoutePrefix::kIsNotUla)) - { - contains = true; - break; - } - } - - return contains; -} - -bool RoutingManager::RxRaTracker::ContainsOnLinkPrefix(OnLinkPrefix::UlaChecker aUlaChecker) const -{ - bool contains = false; - - for (const Router &router : mRouters) - { - if (router.mOnLinkPrefixes.ContainsMatching(aUlaChecker)) - { - contains = true; - break; - } - } - - return contains; -} - -bool RoutingManager::RxRaTracker::ContainsNonUlaOnLinkPrefix(void) const -{ - return ContainsOnLinkPrefix(OnLinkPrefix::kIsNotUla); -} - -bool RoutingManager::RxRaTracker::ContainsUlaOnLinkPrefix(void) const -{ - return ContainsOnLinkPrefix(OnLinkPrefix::kIsUla); -} - -void RoutingManager::RxRaTracker::FindFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const -{ - // Find the smallest preferred on-link prefix entry in the table - // and return it in `aPrefix`. If there is none, `aPrefix` is - // cleared (prefix length is set to zero). - - aPrefix.Clear(); - - for (const Router &router : mRouters) - { - for (const OnLinkPrefix &entry : router.mOnLinkPrefixes) - { - if (entry.IsDeprecated() || (entry.GetPreferredLifetime() < kFavoredOnLinkPrefixMinPreferredLifetime)) - { - continue; - } - - if ((aPrefix.GetLength() == 0) || (entry.GetPrefix() < aPrefix)) - { - aPrefix = entry.GetPrefix(); - } - } - } -} - void RoutingManager::RxRaTracker::HandleLocalOnLinkPrefixChanged(void) { const Ip6::Prefix &prefix = Get().mOnLinkPrefixManager.GetLocalPrefix(); @@ -1390,9 +1307,7 @@ void RoutingManager::RxRaTracker::HandleLocalOnLinkPrefixChanged(void) VerifyOrExit(didRemove); - RemoveRoutersWithNoEntriesOrFlags(); - - SignalTableChanged(); + Evaluate(); exit: return; @@ -1419,8 +1334,7 @@ void RoutingManager::RxRaTracker::HandleNetDataChange(void) if (didRemove) { - RemoveRoutersWithNoEntriesOrFlags(); - SignalTableChanged(); + Evaluate(); } } @@ -1436,7 +1350,6 @@ void RoutingManager::RxRaTracker::RemoveOrDeprecateOldEntries(TimeMilli aTimeThr if (entry.GetLastUpdateTime() <= aTimeThreshold) { entry.ClearPreferredLifetime(); - SignalTableChanged(); } } @@ -1445,7 +1358,6 @@ void RoutingManager::RxRaTracker::RemoveOrDeprecateOldEntries(TimeMilli aTimeThr if (entry.GetLastUpdateTime() <= aTimeThreshold) { entry.ClearValidLifetime(); - SignalTableChanged(); } } } @@ -1453,45 +1365,56 @@ void RoutingManager::RxRaTracker::RemoveOrDeprecateOldEntries(TimeMilli aTimeThr if (mLocalRaHeader.IsValid() && (mLocalRaHeaderUpdateTime <= aTimeThreshold)) { mLocalRaHeader.Clear(); - SignalTableChanged(); } - RemoveExpiredEntries(); + Evaluate(); } -void RoutingManager::RxRaTracker::RemoveOrDeprecateEntriesFromInactiveRouters(void) +void RoutingManager::RxRaTracker::Evaluate(void) { - // Remove route prefix entries and deprecate on-link prefix entries - // in the table for routers that have reached the max NS probe - // attempts and considered as inactive. + DecisionFactors oldFactors = mDecisionFactors; + TimeMilli now = TimerMilli::GetNow(); + NextFireTime routerTimeout(now); + NextFireTime entryExpireTime(now); + NextFireTime staleTime(now); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Remove expired prefix entries in routers and then remove any + // router that has with no prefix entries or flags. + + mRouters.RemoveAndFreeAllMatching(Router::EmptyChecker(now)); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Determine decision factors (favored on-link prefix, has any + // ULA/non-ULA on-link/route prefix, M/O flags). + + mDecisionFactors.Clear(); for (Router &router : mRouters) { - if (router.mNsProbeCount <= Router::kMaxNsProbes) - { - continue; - } + mDecisionFactors.UpdateFlagsFrom(router); for (OnLinkPrefix &entry : router.mOnLinkPrefixes) { - if (!entry.IsDeprecated()) - { - entry.ClearPreferredLifetime(); - SignalTableChanged(); - } + mDecisionFactors.UpdateFrom(entry); + entry.SetStaleTimeCalculated(false); } for (RoutePrefix &entry : router.mRoutePrefixes) { - entry.ClearValidLifetime(); + mDecisionFactors.UpdateFrom(entry); + entry.SetStaleTimeCalculated(false); } } - RemoveExpiredEntries(); -} + if (oldFactors != mDecisionFactors) + { + mSignalTask.Post(); + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Schedule timers -void RoutingManager::RxRaTracker::ScheduleStaleTimer(void) -{ // If multiple routers advertise the same on-link or route prefix, // the stale time for the prefix is determined by the latest stale // time among all corresponding entries. @@ -1502,25 +1425,17 @@ void RoutingManager::RxRaTracker::ScheduleStaleTimer(void) // their entries, `DetermineStaleTimeFor()` will consider all // matching entries and mark "StaleTimeCalculated" flag on them. - NextFireTime staleTime; - - for (Router &router : mRouters) + for (const Router &router : mRouters) { - for (OnLinkPrefix &entry : router.mOnLinkPrefixes) + if (router.ShouldCheckReachability()) { - entry.SetStaleTimeCalculated(false); - } - - for (RoutePrefix &entry : router.mRoutePrefixes) - { - entry.SetStaleTimeCalculated(false); + routerTimeout.UpdateIfEarlier(router.mTimeout); } - } - for (const Router &router : mRouters) - { for (const OnLinkPrefix &entry : router.mOnLinkPrefixes) { + entryExpireTime.UpdateIfEarlier(entry.GetExpireTime()); + if (!entry.IsStaleTimeCalculated()) { DetermineStaleTimeFor(entry, staleTime); @@ -1529,6 +1444,8 @@ void RoutingManager::RxRaTracker::ScheduleStaleTimer(void) for (const RoutePrefix &entry : router.mRoutePrefixes) { + entryExpireTime.UpdateIfEarlier(entry.GetExpireTime()); + if (!entry.IsStaleTimeCalculated()) { DetermineStaleTimeFor(entry, staleTime); @@ -1548,6 +1465,8 @@ void RoutingManager::RxRaTracker::ScheduleStaleTimer(void) staleTime.UpdateIfEarlier(CalculateExpirationTime(mLocalRaHeaderUpdateTime, interval)); } + mRouterTimer.FireAt(routerTimeout); + mExpirationTimer.FireAt(entryExpireTime); mStaleTimer.FireAt(staleTime); } @@ -1621,58 +1540,9 @@ void RoutingManager::RxRaTracker::HandleStaleTimer(void) return; } -void RoutingManager::RxRaTracker::RemoveRoutersWithNoEntriesOrFlags(void) -{ - mRouters.RemoveAndFreeAllMatching(Router::kContainsNoEntriesOrFlags); -} - -void RoutingManager::RxRaTracker::HandleExpirationTimer(void) { RemoveExpiredEntries(); } - -void RoutingManager::RxRaTracker::RemoveExpiredEntries(void) -{ - NextFireTime nextExpireTime; - bool didRemove = false; - - for (Router &router : mRouters) - { - LifetimedPrefix::ExpirationChecker expirationChecker(nextExpireTime.GetNow()); - - didRemove |= router.mOnLinkPrefixes.RemoveAndFreeAllMatching(expirationChecker); - didRemove |= router.mRoutePrefixes.RemoveAndFreeAllMatching(expirationChecker); - } - - RemoveRoutersWithNoEntriesOrFlags(); - - if (didRemove) - { - SignalTableChanged(); - } - - // Determine the next expire time and schedule timer. +void RoutingManager::RxRaTracker::HandleExpirationTimer(void) { Evaluate(); } - for (const Router &router : mRouters) - { - for (const OnLinkPrefix &entry : router.mOnLinkPrefixes) - { - nextExpireTime.UpdateIfEarlier(entry.GetExpireTime()); - } - - for (const RoutePrefix &entry : router.mRoutePrefixes) - { - nextExpireTime.UpdateIfEarlier(entry.GetExpireTime()); - } - } - - mExpirationTimer.FireAt(nextExpireTime); -} - -void RoutingManager::RxRaTracker::SignalTableChanged(void) { mSignalTask.Post(); } - -void RoutingManager::RxRaTracker::HandleSignalTask(void) -{ - ScheduleStaleTimer(); - Get().HandleRaPrefixTableChanged(); -} +void RoutingManager::RxRaTracker::HandleSignalTask(void) { Get().HandleRaPrefixTableChanged(); } void RoutingManager::RxRaTracker::ProcessNeighborAdvertMessage(const NeighborAdvertMessage &aNaMessage) { @@ -1685,65 +1555,57 @@ void RoutingManager::RxRaTracker::ProcessNeighborAdvertMessage(const NeighborAdv LogInfo("Received NA from router %s", router->mAddress.ToString().AsCString()); - UpdateRouterOnRx(*router); + router->ResetReachabilityState(); + + Evaluate(); exit: return; } -void RoutingManager::RxRaTracker::UpdateRouterOnRx(Router &aRouter) -{ - aRouter.mNsProbeCount = 0; - aRouter.mLastUpdateTime = TimerMilli::GetNow(); - - aRouter.mTimeout = aRouter.mLastUpdateTime + Random::NonCrypto::AddJitter(Router::kActiveTimeout, Router::kJitter); - mRouterTimer.FireAtIfEarlier(aRouter.mTimeout); -} - void RoutingManager::RxRaTracker::HandleRouterTimer(void) { - NextFireTime nextTime; + TimeMilli now = TimerMilli::GetNow(); for (Router &router : mRouters) { - if (router.mNsProbeCount > Router::kMaxNsProbes) + if (!router.ShouldCheckReachability() || (router.mTimeout > now)) { continue; } - // Skip NS probes if the router is this device. This prevents - // issues where the platform might not be able to receive and - // process the NA messages from the local device itself. + router.mNsProbeCount++; - if (router.mIsLocalDevice) + if (router.IsReachable()) { - continue; + router.mTimeout = now + ((router.mNsProbeCount < Router::kMaxNsProbes) ? Router::kNsProbeRetryInterval + : Router::kNsProbeTimeout); + SendNeighborSolicitToRouter(router); } - - if (router.mTimeout <= nextTime.GetNow()) + else { - router.mNsProbeCount++; + LogInfo("No response to all Neighbor Solicitations attempts from router %s - marking it unreachable", + router.mAddress.ToString().AsCString()); + + // Remove route prefix entries and deprecate on-link prefix entries + // of the unreachable router. - if (router.mNsProbeCount > Router::kMaxNsProbes) + for (OnLinkPrefix &entry : router.mOnLinkPrefixes) { - LogInfo("No response to all Neighbor Solicitations attempts from router %s", - router.mAddress.ToString().AsCString()); - continue; + if (!entry.IsDeprecated()) + { + entry.ClearPreferredLifetime(); + } } - router.mTimeout = - nextTime.GetNow() + ((router.mNsProbeCount < Router::kMaxNsProbes) ? Router::kNsProbeRetryInterval - : Router::kNsProbeTimeout); - - SendNeighborSolicitToRouter(router); + for (RoutePrefix &entry : router.mRoutePrefixes) + { + entry.ClearValidLifetime(); + } } - - nextTime.UpdateIfEarlier(router.mTimeout); } - RemoveOrDeprecateEntriesFromInactiveRouters(); - - mRouterTimer.FireAt(nextTime); + Evaluate(); } void RoutingManager::RxRaTracker::SendNeighborSolicitToRouter(const Router &aRouter) @@ -1765,39 +1627,16 @@ void RoutingManager::RxRaTracker::SendNeighborSolicitToRouter(const Router &aRou return; } -void RoutingManager::RxRaTracker::DetermineAndSetFlags(RouterAdvert::Header &aHeader) const +void RoutingManager::RxRaTracker::SetHeaderFlagsOn(RouterAdvert::Header &aHeader) const { - // Determine the `M` and `O` flags to include in the RA message - // header to be emitted. - // - // If any discovered router on infrastructure which is not itself a - // stub router (e.g., another Thread BR) includes the `M` or `O` - // flag, we also include the same flag. - // - // If a router has failed to respond to max number of NS probe - // attempts, we consider it as offline and ignore its flags. - - for (const Router &router : mRouters) + if (mDecisionFactors.mHeaderManagedAddressConfigFlag) { - if (router.mStubRouterFlag) - { - continue; - } - - if (router.mNsProbeCount > Router::kMaxNsProbes) - { - continue; - } - - if (router.mManagedAddressConfigFlag) - { - aHeader.SetManagedAddressConfigFlag(); - } + aHeader.SetManagedAddressConfigFlag(); + } - if (router.mOtherConfigFlag) - { - aHeader.SetOtherConfigFlag(); - } + if (mDecisionFactors.mHeaderOtherConfigFlag) + { + aHeader.SetOtherConfigFlag(); } } @@ -1941,20 +1780,42 @@ Error RoutingManager::RxRaTracker::Iterator::AdvanceToNextEntry(void) //--------------------------------------------------------------------------------------------------------------------- // RxRaTracker::Router -bool RoutingManager::RxRaTracker::Router::Matches(EmptyChecker aChecker) const +bool RoutingManager::RxRaTracker::Router::ShouldCheckReachability(void) const +{ + // Perform reachability check (send NS probes) only if the router: + // - Is not already marked as unreachable (due to failed NS probes) + // - Is not the local device itself (to avoid potential issues with + // the platform receiving/processing NAs from itself). + + return IsReachable() && !mIsLocalDevice; +} + +void RoutingManager::RxRaTracker::Router::ResetReachabilityState(void) { - // Checks whether or not a `Router` instance has any useful info. An - // entry can be removed if it does not advertise M or O flags and + // Called when an RA or NA is received and processed. + + mNsProbeCount = 0; + mLastUpdateTime = TimerMilli::GetNow(); + mTimeout = mLastUpdateTime + Random::NonCrypto::AddJitter(kReachableTimeout, kJitter); +} + +bool RoutingManager::RxRaTracker::Router::Matches(const EmptyChecker &aChecker) +{ + // First removes all expired on-link or router prefixes. Then + // checks whether or not the router has any useful info. + + bool hasFlags = false; + + mOnLinkPrefixes.RemoveAndFreeAllMatching(aChecker); + mRoutePrefixes.RemoveAndFreeAllMatching(aChecker); + + // Router can be removed if it does not advertise M or O flags and // also does not have any advertised prefix entries (RIO/PIO). If // the router already failed to respond to max NS probe attempts, // we consider it as offline and therefore do not consider its // flags anymore. - OT_UNUSED_VARIABLE(aChecker); - - bool hasFlags = false; - - if (mNsProbeCount <= kMaxNsProbes) + if (IsReachable()) { hasFlags = (mManagedAddressConfigFlag || mOtherConfigFlag); } @@ -1970,6 +1831,69 @@ void RoutingManager::RxRaTracker::Router::CopyInfoTo(RouterEntry &aEntry, TimeMi aEntry.mOtherConfigFlag = mOtherConfigFlag; aEntry.mStubRouterFlag = mStubRouterFlag; aEntry.mIsLocalDevice = mIsLocalDevice; + aEntry.mIsReachable = IsReachable(); +} + +//--------------------------------------------------------------------------------------------------------------------- +// RxRaTracker::DecisionFactors + +void RoutingManager::RxRaTracker::DecisionFactors::UpdateFlagsFrom(const Router &aRouter) +{ + // Determine the `M` and `O` flags to include in the RA message + // header to be emitted. + // + // If any discovered router on infrastructure which is not itself a + // stub router (e.g., another Thread BR) includes the `M` or `O` + // flag, we also include the same flag. + + VerifyOrExit(!aRouter.mStubRouterFlag); + VerifyOrExit(aRouter.IsReachable()); + + if (aRouter.mManagedAddressConfigFlag) + { + mHeaderManagedAddressConfigFlag = true; + } + + if (aRouter.mOtherConfigFlag) + { + mHeaderOtherConfigFlag = true; + } + +exit: + return; +} + +void RoutingManager::RxRaTracker::DecisionFactors::UpdateFrom(const OnLinkPrefix &aOnLinkPrefix) +{ + if (aOnLinkPrefix.GetPrefix().IsUniqueLocal()) + { + mHasUlaOnLink = true; + } + else + { + mHasNonUlaOnLink = true; + } + + // Determine favored on-link prefix + + VerifyOrExit(!aOnLinkPrefix.IsDeprecated()); + VerifyOrExit(aOnLinkPrefix.GetPreferredLifetime() >= kFavoredOnLinkPrefixMinPreferredLifetime); + + if ((mFavoredOnLinkPrefix.GetLength() == 0) || (aOnLinkPrefix.GetPrefix() < mFavoredOnLinkPrefix)) + { + mFavoredOnLinkPrefix = aOnLinkPrefix.GetPrefix(); + } + +exit: + return; +} + +void RoutingManager::RxRaTracker::DecisionFactors::UpdateFrom(const RoutePrefix &aRoutePrefix) +{ + if (!mHasNonUlaRoute) + { + mHasNonUlaRoute = !aRoutePrefix.GetPrefix().IsUniqueLocal(); + } } //--------------------------------------------------------------------------------------------------------------------- @@ -2396,7 +2320,7 @@ void RoutingManager::OnLinkPrefixManager::Evaluate(void) { VerifyOrExit(!Get().mRsSender.IsInProgress()); - Get().mRxRaTracker.FindFavoredOnLinkPrefix(mFavoredDiscoveredPrefix); + mFavoredDiscoveredPrefix = Get().mRxRaTracker.GetFavoredOnLinkPrefix(); if ((mFavoredDiscoveredPrefix.GetLength() == 0) || (mFavoredDiscoveredPrefix == mLocalPrefix)) { @@ -2448,11 +2372,7 @@ void RoutingManager::OnLinkPrefixManager::HandleRaPrefixTableChanged(void) // prefix has changed, we trigger a re-evaluation of the routing // policy. - Ip6::Prefix newFavoredPrefix; - - Get().mRxRaTracker.FindFavoredOnLinkPrefix(newFavoredPrefix); - - if (newFavoredPrefix != mFavoredDiscoveredPrefix) + if (Get().mRxRaTracker.GetFavoredOnLinkPrefix() != mFavoredDiscoveredPrefix) { Get().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay); } diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 456326e29..dec8ccc5e 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -54,6 +54,7 @@ #include "border_router/infra_if.hpp" #include "common/array.hpp" #include "common/callback.hpp" +#include "common/equatable.hpp" #include "common/error.hpp" #include "common/heap_allocatable.hpp" #include "common/heap_array.hpp" @@ -658,12 +659,6 @@ class RoutingManager : public InstanceLocator // base class for `OnLinkPrefix` or `RoutePrefix`. public: - enum UlaChecker : bool - { - kIsNotUla = false, - kIsUla = true, - }; - struct ExpirationChecker { explicit ExpirationChecker(TimeMilli aNow) { mNow = aNow; } @@ -677,7 +672,6 @@ class RoutingManager : public InstanceLocator TimeMilli GetExpireTime(void) const { return CalculateExpirationTime(mValidLifetime); } bool Matches(const Ip6::Prefix &aPrefix) const { return (mPrefix == aPrefix); } - bool Matches(const UlaChecker &aIsUla) const { return (mPrefix.IsUniqueLocal() == aIsUla); } bool Matches(const ExpirationChecker &aChecker) const { return (GetExpireTime() <= aChecker.mNow); } void SetStaleTimeCalculated(bool aFlag) { mStaleTimeCalculated = aFlag; } @@ -761,25 +755,27 @@ class RoutingManager : public InstanceLocator RouterAdvOrigin aRaOrigin); void ProcessNeighborAdvertMessage(const NeighborAdvertMessage &aNaMessage); - bool ContainsDefaultOrNonUlaRoutePrefix(void) const; - bool ContainsNonUlaOnLinkPrefix(void) const; - bool ContainsUlaOnLinkPrefix(void) const; + // Decision factors + bool ContainsDefaultOrNonUlaRoutePrefix(void) const { return mDecisionFactors.mHasNonUlaRoute; } + bool ContainsNonUlaOnLinkPrefix(void) const { return mDecisionFactors.mHasNonUlaOnLink; } + bool ContainsUlaOnLinkPrefix(void) const { return mDecisionFactors.mHasUlaOnLink; } - void FindFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const; - - void HandleLocalOnLinkPrefixChanged(void); - void HandleNetDataChange(void); - - void RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold); + const Ip6::Prefix &GetFavoredOnLinkPrefix(void) const { return mDecisionFactors.mFavoredOnLinkPrefix; } + void SetHeaderFlagsOn(RouterAdvert::Header &aHeader) const; const RouterAdvert::Header &GetLocalRaHeaderToMirror(void) const { return mLocalRaHeader; } - void DetermineAndSetFlags(RouterAdvert::Header &aHeader) const; - + // Iterating over discovered items void InitIterator(PrefixTableIterator &aIterator) const; Error GetNextEntry(PrefixTableIterator &aIterator, PrefixTableEntry &aEntry) const; Error GetNextRouter(PrefixTableIterator &aIterator, RouterEntry &aEntry) const; + // Callbacks notifying of changes + void RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold); + void HandleLocalOnLinkPrefixChanged(void); + void HandleNetDataChange(void); + + // Tasklet or timer callbacks void HandleSignalTask(void); void HandleExpirationTimer(void); void HandleStaleTimer(void); @@ -811,9 +807,9 @@ class RoutingManager : public InstanceLocator struct Router : public Clearable { - // The timeout (in msec) for router staying in active state + // The timeout (in msec) for router to be considered reachable // before starting the Neighbor Solicitation (NS) probes. - static constexpr uint32_t kActiveTimeout = OPENTHREAD_CONFIG_BORDER_ROUTING_ROUTER_ACTIVE_CHECK_TIMEOUT; + static constexpr uint32_t kReachableTimeout = OPENTHREAD_CONFIG_BORDER_ROUTING_ROUTER_ACTIVE_CHECK_TIMEOUT; static constexpr uint8_t kMaxNsProbes = 5; // Max number of NS probe attempts. static constexpr uint32_t kNsProbeRetryInterval = 1000; // In msec. Time between NS probe attempts. @@ -822,13 +818,13 @@ class RoutingManager : public InstanceLocator static_assert(kMaxNsProbes < 255, "kMaxNsProbes MUST not be 255"); - enum EmptyChecker : uint8_t - { - kContainsNoEntriesOrFlags - }; + typedef LifetimedPrefix::ExpirationChecker EmptyChecker; + bool IsReachable(void) const { return mNsProbeCount <= kMaxNsProbes; } + bool ShouldCheckReachability(void) const; + void ResetReachabilityState(void); bool Matches(const Ip6::Address &aAddress) const { return aAddress == mAddress; } - bool Matches(EmptyChecker aChecker) const; + bool Matches(const EmptyChecker &aChecker); void CopyInfoTo(RouterEntry &aEntry, TimeMilli aNow) const; using OnLinkPrefixList = OwningList>; @@ -909,19 +905,32 @@ class RoutingManager : public InstanceLocator //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + struct DecisionFactors : public Clearable, public Equatable + { + DecisionFactors(void) { Clear(); } + + bool HasFavoredOnLink(void) const { return (mFavoredOnLinkPrefix.GetLength() != 0); } + void UpdateFlagsFrom(const Router &aRouter); + void UpdateFrom(const OnLinkPrefix &aOnLinkPrefix); + void UpdateFrom(const RoutePrefix &aRoutePrefix); + + Ip6::Prefix mFavoredOnLinkPrefix; + bool mHasNonUlaRoute : 1; + bool mHasNonUlaOnLink : 1; + bool mHasUlaOnLink : 1; + bool mHeaderManagedAddressConfigFlag : 1; + bool mHeaderOtherConfigFlag : 1; + }; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + void ProcessRaHeader(const RouterAdvert::Header &aRaHeader, Router &aRouter, RouterAdvOrigin aRaOrigin); void ProcessPrefixInfoOption(const PrefixInfoOption &aPio, Router &aRouter); void ProcessRouteInfoOption(const RouteInfoOption &aRio, Router &aRouter); void ProcessRaFlagsExtOption(const RaFlagsExtOption &aFlagsOption, Router &aRouter); - bool ContainsOnLinkPrefix(OnLinkPrefix::UlaChecker aUlaChecker) const; - void RemoveOrDeprecateEntriesFromInactiveRouters(void); - void RemoveRoutersWithNoEntriesOrFlags(void); - void RemoveExpiredEntries(void); - void SignalTableChanged(void); - void ScheduleStaleTimer(void); + void Evaluate(void); void DetermineStaleTimeFor(const OnLinkPrefix &aPrefix, NextFireTime &aStaleTime); void DetermineStaleTimeFor(const RoutePrefix &aPrefix, NextFireTime &aStaleTime); - void UpdateRouterOnRx(Router &aRouter); void SendNeighborSolicitToRouter(const Router &aRouter); #if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE template Entry *AllocateEntry(void) { return Entry::Allocate(); } @@ -935,6 +944,7 @@ class RoutingManager : public InstanceLocator using RouterTimer = TimerMilliIn; using RouterList = OwningList>; + DecisionFactors mDecisionFactors; RouterList mRouters; ExpirationTimer mExpirationTimer; StaleTimer mStaleTimer; @@ -942,6 +952,7 @@ class RoutingManager : public InstanceLocator SignalTask mSignalTask; RouterAdvert::Header mLocalRaHeader; TimeMilli mLocalRaHeaderUpdateTime; + #if !OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE Pool mEntryPool; Pool, kMaxRouters> mRouterPool; diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp index 941f6086b..38ba54e9b 100644 --- a/src/core/coap/coap.cpp +++ b/src/core/coap/coap.cpp @@ -1693,7 +1693,7 @@ Resource::Resource(Uri aUri, RequestHandler aHandler, void *aContext) Coap::Coap(Instance &aInstance) : CoapBase(aInstance, &Coap::Send) - , mSocket(aInstance) + , mSocket(aInstance, *this) { } @@ -1704,7 +1704,7 @@ Error Coap::Start(uint16_t aPort, Ip6::NetifIdentifier aNetifIdentifier) VerifyOrExit(!mSocket.IsBound()); - SuccessOrExit(error = mSocket.Open(&Coap::HandleUdpReceive, this)); + SuccessOrExit(error = mSocket.Open()); socketOpened = true; SuccessOrExit(error = mSocket.Bind(aPort, aNetifIdentifier)); @@ -1731,9 +1731,9 @@ Error Coap::Stop(void) return error; } -void Coap::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +void Coap::HandleUdpReceive(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - static_cast(aContext)->Receive(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); + Receive(AsCoapMessage(&aMessage), aMessageInfo); } Error Coap::Send(CoapBase &aCoapBase, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) diff --git a/src/core/coap/coap.hpp b/src/core/coap/coap.hpp index 9a08916a1..aeae3bb74 100644 --- a/src/core/coap/coap.hpp +++ b/src/core/coap/coap.hpp @@ -997,11 +997,14 @@ class Coap : public CoapBase Error Stop(void); protected: - Ip6::Udp::Socket mSocket; + void HandleUdpReceive(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + + using CoapSocket = Ip6::Udp::SocketIn; + + CoapSocket mSocket; private: static Error Send(CoapBase &aCoapBase, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); Error Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); }; diff --git a/src/core/common/tlvs.cpp b/src/core/common/tlvs.cpp index 04756d24a..2848e7f0c 100644 --- a/src/core/common/tlvs.cpp +++ b/src/core/common/tlvs.cpp @@ -56,6 +56,24 @@ const uint8_t *Tlv::GetValue(void) const Error Tlv::AppendTo(Message &aMessage) const { return aMessage.AppendBytes(this, static_cast(GetSize())); } +Error Tlv::ParseAndSkipTlv(const Message &aMessage, uint16_t &aOffset) +{ + Error error; + ParsedInfo info; + + SuccessOrExit(error = info.ParseFrom(aMessage, aOffset)); + + // `ParseFrom()` has already validated that the entire TLV is + // present within `aMessage`. This ensures that `aOffset + mSize` + // is less than `aMessage.GetLength()`, and therefore we cannot + // have an overflow here. + + aOffset += info.mSize; + +exit: + return error; +} + Error Tlv::FindTlv(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tlv &aTlv) { uint16_t offset; diff --git a/src/core/common/tlvs.hpp b/src/core/common/tlvs.hpp index 1acc80694..7baf529a3 100644 --- a/src/core/common/tlvs.hpp +++ b/src/core/common/tlvs.hpp @@ -244,6 +244,21 @@ class Tlv //------------------------------------------------------------------------------------------------------------------ // Static methods for reading/finding/appending TLVs in a `Message`. + /** + * Parses a TLV in a message at a given offset, validating that it is fully contained within the message and then + * updating the offset to skip over the entire parsed TLV. + * + * Can be used independent of whether the read TLV (from the message) is an Extended TLV or not. + * + * @param[in] aMessage The message to read from. + * @param[in,out] aOffset The offset to read from. On success, it is updated to point after the parsed TLV. + * + * @retval kErrorNone Successfully parsed a TLV from @p aMessage. @p aOffset is updated. + * @retval kErrorParse The TLV was not well-formed or was not fully contained in @p aMessage. + * + */ + static Error ParseAndSkipTlv(const Message &aMessage, uint16_t &aOffset); + /** * Reads a TLV's value in a message at a given offset expecting a minimum length for the value. * diff --git a/src/core/config/dnssd_server.h b/src/core/config/dnssd_server.h index 36b5620f3..fb5aa3f00 100644 --- a/src/core/config/dnssd_server.h +++ b/src/core/config/dnssd_server.h @@ -85,6 +85,16 @@ #define OPENTHREAD_CONFIG_DNSSD_QUERY_TIMEOUT 6000 #endif +/** + * @def OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + * + * Define to 1 to enable DNS-SD Discovery Proxy support. + * + */ +#ifndef OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE +#define OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE 0 +#endif + /** * @def OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE * diff --git a/src/core/diags/README.md b/src/core/diags/README.md index 2ddb658ac..ee447b3f9 100644 --- a/src/core/diags/README.md +++ b/src/core/diags/README.md @@ -77,6 +77,15 @@ Stop transmitting continuous carrier wave. Done ``` +### diag frame \ + +Set the frame (hex encoded) to be used by `diag send` and `diag repeat`. The frame may be overwritten by `diag send` and `diag repeat`. + +```bash +> diag frame 11223344 +Done +``` + ### diag stream start Start transmitting a stream of characters. @@ -143,11 +152,11 @@ RawPowerSetting: 223344 Done ``` -### diag send \ \ +### diag send \ [length] -Transmit a fixed number of packets with fixed length. +Transmit a fixed number of packets. -Length parameter has to be in range [3, 127]. +Send the frame set by `diag frame` if length is omitted. Otherwise overwrite the frame set by `diag frame` and send a frame of the given length(MUST be in range [3, 127]). ```bash > diag send 20 100 @@ -155,11 +164,11 @@ sending 0x14 packet(s), length 0x64 status 0x00 ``` -### diag repeat \ \ +### diag repeat \ [length] Transmit packets repeatedly with a fixed interval. -Length parameter has to be in range [3, 127]. +Send the frame set by `diag frame` if length is omitted. Otherwise overwrite the frame set by `diag frame` and send a frame of the given length (MUST be in range [3, 127]). ```bash > diag repeat 100 100 diff --git a/src/core/diags/factory_diags.cpp b/src/core/diags/factory_diags.cpp index 78845f1aa..d5a0ee288 100644 --- a/src/core/diags/factory_diags.cpp +++ b/src/core/diags/factory_diags.cpp @@ -32,6 +32,7 @@ */ #include "factory_diags.hpp" +#include "common/error.hpp" #if OPENTHREAD_CONFIG_DIAG_ENABLE @@ -49,17 +50,11 @@ #include "utils/parse_cmdline.hpp" OT_TOOL_WEAK -otError otPlatDiagProcess(otInstance *aInstance, - uint8_t aArgsLength, - char *aArgs[], - char *aOutput, - size_t aOutputMaxLen) +otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); OT_UNUSED_VARIABLE(aInstance); - OT_UNUSED_VARIABLE(aOutput); - OT_UNUSED_VARIABLE(aOutputMaxLen); return ot::kErrorInvalidCommand; } @@ -84,10 +79,12 @@ const struct Diags::Command Diags::sCommands[] = { Diags::Diags(Instance &aInstance) : InstanceLocator(aInstance) + , mOutputCallback(nullptr) + , mOutputContext(nullptr) { } -Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; long value; @@ -100,11 +97,11 @@ Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, s otPlatDiagChannelSet(static_cast(value)); exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; long value; @@ -116,35 +113,39 @@ Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz otPlatDiagTxPowerSet(static_cast(value)); exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessEcho(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessEcho(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; if (aArgsLength == 1) { - snprintf(aOutput, aOutputMaxLen, "%s\r\n", aArgs[0]); + Output("%s\r\n", aArgs[0]); } else if ((aArgsLength == 2) && (strcmp(aArgs[0], "-n") == 0)) { - const uint8_t kReservedLen = 3; // 1 byte '\r', 1 byte '\n' and 1 byte '\0' - uint32_t outputMaxLen = static_cast(aOutputMaxLen) - kReservedLen; - long value; - uint32_t i; - uint32_t number; + static constexpr uint8_t kReservedLen = 1; // 1 byte '\0' + static constexpr uint16_t kOutputLen = OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE; + static constexpr uint16_t kOutputMaxLen = kOutputLen - kReservedLen; + char output[kOutputLen]; + long value; + uint32_t i; + uint32_t number; SuccessOrExit(error = ParseLong(aArgs[1], value)); - number = Min(static_cast(value), outputMaxLen); + number = Min(static_cast(value), static_cast(kOutputMaxLen)); for (i = 0; i < number; i++) { - aOutput[i] = '0' + i % 10; + output[i] = '0' + i % 10; } - snprintf(&aOutput[i], aOutputMaxLen - i, "\r\n"); + output[number] = '\0'; + + Output("%s\r\n", output); } else { @@ -152,28 +153,24 @@ Error Diags::ProcessEcho(uint8_t aArgsLength, char *aArgs[], char *aOutput, size } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); - OT_UNUSED_VARIABLE(aOutput); - OT_UNUSED_VARIABLE(aOutputMaxLen); otPlatDiagModeSet(true); return kErrorNone; } -Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); - OT_UNUSED_VARIABLE(aOutput); - OT_UNUSED_VARIABLE(aOutputMaxLen); otPlatDiagModeSet(false); @@ -187,6 +184,7 @@ extern "C" void otPlatDiagAlarmFired(otInstance *aInstance) { otPlatDiagAlarmCal const struct Diags::Command Diags::sCommands[] = { {"channel", &Diags::ProcessChannel}, {"cw", &Diags::ProcessContinuousWave}, + {"frame", &Diags::ProcessFrame}, {"gpio", &Diags::ProcessGpio}, {"power", &Diags::ProcessPower}, {"powersettings", &Diags::ProcessPowerSettings}, @@ -208,13 +206,34 @@ Diags::Diags(Instance &aInstance) , mChannel(20) , mTxPower(0) , mTxLen(0) + , mIsTxPacketSet(false) , mRepeatActive(false) , mDiagSendOn(false) + , mOutputCallback(nullptr) + , mOutputContext(nullptr) { mStats.Clear(); } -Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessFrame(uint8_t aArgsLength, char *aArgs[]) +{ + Error error = kErrorNone; + uint16_t size = OT_RADIO_FRAME_MAX_SIZE; + + VerifyOrExit(aArgsLength == 1, error = kErrorInvalidArgs); + + SuccessOrExit(error = Utils::CmdLineParser::ParseAsHexString(aArgs[0], size, mTxPacket->mPsdu)); + VerifyOrExit(size <= OT_RADIO_FRAME_MAX_SIZE, error = kErrorInvalidArgs); + VerifyOrExit(size >= OT_RADIO_FRAME_MIN_SIZE, error = kErrorInvalidArgs); + mTxPacket->mLength = size; + mIsTxPacketSet = true; + +exit: + AppendErrorResult(error); + return error; +} + +Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; @@ -222,7 +241,7 @@ Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, s if (aArgsLength == 0) { - snprintf(aOutput, aOutputMaxLen, "channel: %d\r\n", mChannel); + Output("channel: %d\r\n", mChannel); } else { @@ -235,15 +254,15 @@ Error Diags::ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, s IgnoreError(Get().Receive(mChannel)); otPlatDiagChannelSet(mChannel); - snprintf(aOutput, aOutputMaxLen, "set channel to %d\r\nstatus 0x%02x\r\n", mChannel, error); + Output("set channel to %d\r\nstatus 0x%02x\r\n", mChannel, error); } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; @@ -251,7 +270,7 @@ Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz if (aArgsLength == 0) { - snprintf(aOutput, aOutputMaxLen, "tx power: %d dBm\r\n", mTxPower); + Output("tx power: %d dBm\r\n", mTxPower); } else { @@ -263,15 +282,15 @@ Error Diags::ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz SuccessOrExit(error = Get().SetTransmitPower(mTxPower)); otPlatDiagTxPowerSet(mTxPower); - snprintf(aOutput, aOutputMaxLen, "set tx power to %d dBm\r\nstatus 0x%02x\r\n", mTxPower, error); + Output("set tx power to %d dBm\r\nstatus 0x%02x\r\n", mTxPower, error); } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessRepeat(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessRepeat(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; @@ -282,18 +301,31 @@ Error Diags::ProcessRepeat(uint8_t aArgsLength, char *aArgs[], char *aOutput, si { otPlatAlarmMilliStop(&GetInstance()); mRepeatActive = false; - snprintf(aOutput, aOutputMaxLen, "repeated packet transmission is stopped\r\nstatus 0x%02x\r\n", error); + Output("repeated packet transmission is stopped\r\nstatus 0x%02x\r\n", error); } else { long value; - VerifyOrExit(aArgsLength == 2, error = kErrorInvalidArgs); + VerifyOrExit(aArgsLength >= 1, error = kErrorInvalidArgs); SuccessOrExit(error = ParseLong(aArgs[0], value)); mTxPeriod = static_cast(value); - SuccessOrExit(error = ParseLong(aArgs[1], value)); + if (aArgsLength >= 2) + { + SuccessOrExit(error = ParseLong(aArgs[1], value)); + mIsTxPacketSet = false; + } + else if (mIsTxPacketSet) + { + value = mTxPacket->mLength; + } + else + { + ExitNow(error = kErrorInvalidArgs); + } + VerifyOrExit(value <= OT_RADIO_FRAME_MAX_SIZE, error = kErrorInvalidArgs); VerifyOrExit(value >= OT_RADIO_FRAME_MIN_SIZE, error = kErrorInvalidArgs); mTxLen = static_cast(value); @@ -301,41 +333,54 @@ Error Diags::ProcessRepeat(uint8_t aArgsLength, char *aArgs[], char *aOutput, si mRepeatActive = true; uint32_t now = otPlatAlarmMilliGetNow(); otPlatAlarmMilliStartAt(&GetInstance(), now, mTxPeriod); - snprintf(aOutput, aOutputMaxLen, "sending packets of length %#x at the delay of %#x ms\r\nstatus 0x%02x\r\n", - static_cast(mTxLen), static_cast(mTxPeriod), error); + Output("sending packets of length %#x at the delay of %#x ms\r\nstatus 0x%02x\r\n", static_cast(mTxLen), + static_cast(mTxPeriod), error); } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessSend(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessSend(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; long value; VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState); - VerifyOrExit(aArgsLength == 2, error = kErrorInvalidArgs); + VerifyOrExit(aArgsLength >= 1, error = kErrorInvalidArgs); SuccessOrExit(error = ParseLong(aArgs[0], value)); mTxPackets = static_cast(value); - SuccessOrExit(error = ParseLong(aArgs[1], value)); + if (aArgsLength >= 2) + { + SuccessOrExit(ParseLong(aArgs[1], value)); + mIsTxPacketSet = false; + } + else if (mIsTxPacketSet) + { + value = mTxPacket->mLength; + } + else + { + ExitNow(error = kErrorInvalidArgs); + } + VerifyOrExit(value <= OT_RADIO_FRAME_MAX_SIZE, error = kErrorInvalidArgs); VerifyOrExit(value >= OT_RADIO_FRAME_MIN_SIZE, error = kErrorInvalidArgs); mTxLen = static_cast(value); - snprintf(aOutput, aOutputMaxLen, "sending %#x packet(s), length %#x\r\nstatus 0x%02x\r\n", - static_cast(mTxPackets), static_cast(mTxLen), error); + Output("sending %#x packet(s), length %#x\r\nstatus 0x%02x\r\n", static_cast(mTxPackets), + static_cast(mTxLen), error); TransmitPacket(); exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); @@ -356,14 +401,14 @@ Error Diags::ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz SuccessOrExit(error = Get().SetTransmitPower(mTxPower)); otPlatDiagModeSet(true); mStats.Clear(); - snprintf(aOutput, aOutputMaxLen, "start diagnostics mode\r\nstatus 0x%02x\r\n", error); + Output("start diagnostics mode\r\nstatus 0x%02x\r\n", error); exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessStats(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessStats(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; @@ -372,26 +417,25 @@ Error Diags::ProcessStats(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz if ((aArgsLength == 1) && (strcmp(aArgs[0], "clear") == 0)) { mStats.Clear(); - snprintf(aOutput, aOutputMaxLen, "stats cleared\r\n"); + Output("stats cleared\r\n"); } else { VerifyOrExit(aArgsLength == 0, error = kErrorInvalidArgs); - snprintf(aOutput, aOutputMaxLen, - "received packets: %d\r\nsent packets: %d\r\n" - "first received packet: rssi=%d, lqi=%d\r\n" - "last received packet: rssi=%d, lqi=%d\r\n", - static_cast(mStats.mReceivedPackets), static_cast(mStats.mSentPackets), - static_cast(mStats.mFirstRssi), static_cast(mStats.mFirstLqi), - static_cast(mStats.mLastRssi), static_cast(mStats.mLastLqi)); + Output("received packets: %d\r\nsent packets: %d\r\n" + "first received packet: rssi=%d, lqi=%d\r\n" + "last received packet: rssi=%d, lqi=%d\r\n", + static_cast(mStats.mReceivedPackets), static_cast(mStats.mSentPackets), + static_cast(mStats.mFirstRssi), static_cast(mStats.mFirstLqi), + static_cast(mStats.mLastRssi), static_cast(mStats.mLastLqi)); } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); @@ -404,35 +448,38 @@ Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[], char *aOutput, size otPlatDiagModeSet(false); Get().SetPromiscuous(false); - snprintf(aOutput, aOutputMaxLen, - "received packets: %d\r\nsent packets: %d\r\n" - "first received packet: rssi=%d, lqi=%d\r\n" - "last received packet: rssi=%d, lqi=%d\r\n" - "\nstop diagnostics mode\r\nstatus 0x%02x\r\n", - static_cast(mStats.mReceivedPackets), static_cast(mStats.mSentPackets), - static_cast(mStats.mFirstRssi), static_cast(mStats.mFirstLqi), - static_cast(mStats.mLastRssi), static_cast(mStats.mLastLqi), error); + Output("received packets: %d\r\nsent packets: %d\r\n" + "first received packet: rssi=%d, lqi=%d\r\n" + "last received packet: rssi=%d, lqi=%d\r\n" + "\nstop diagnostics mode\r\nstatus 0x%02x\r\n", + static_cast(mStats.mReceivedPackets), static_cast(mStats.mSentPackets), + static_cast(mStats.mFirstRssi), static_cast(mStats.mFirstLqi), static_cast(mStats.mLastRssi), + static_cast(mStats.mLastLqi), error); exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } void Diags::TransmitPacket(void) { - mTxPacket->mLength = mTxLen; mTxPacket->mChannel = mChannel; - for (uint8_t i = 0; i < mTxLen; i++) + if (!mIsTxPacketSet) { - mTxPacket->mPsdu[i] = i; + mTxPacket->mLength = mTxLen; + + for (uint8_t i = 0; i < mTxLen; i++) + { + mTxPacket->mPsdu[i] = i; + } } mDiagSendOn = true; IgnoreError(Get().Transmit(*static_cast(mTxPacket))); } -Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorInvalidArgs; @@ -442,7 +489,7 @@ Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz if (strcmp(aArgs[0], "sleep") == 0) { SuccessOrExit(error = Get().Sleep()); - snprintf(aOutput, aOutputMaxLen, "set radio from receive to sleep \r\nstatus 0x%02x\r\n", error); + Output("set radio from receive to sleep \r\nstatus 0x%02x\r\n", error); } else if (strcmp(aArgs[0], "receive") == 0) { @@ -451,8 +498,7 @@ Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz otPlatDiagChannelSet(mChannel); otPlatDiagTxPowerSet(mTxPower); - snprintf(aOutput, aOutputMaxLen, "set radio from sleep to receive on channel %d\r\nstatus 0x%02x\r\n", mChannel, - error); + Output("set radio from sleep to receive on channel %d\r\nstatus 0x%02x\r\n", mChannel, error); } else if (strcmp(aArgs[0], "state") == 0) { @@ -463,29 +509,29 @@ Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz switch (state) { case OT_RADIO_STATE_DISABLED: - snprintf(aOutput, aOutputMaxLen, "disabled\r\n"); + Output("disabled\r\n"); break; case OT_RADIO_STATE_SLEEP: - snprintf(aOutput, aOutputMaxLen, "sleep\r\n"); + Output("sleep\r\n"); break; case OT_RADIO_STATE_RECEIVE: - snprintf(aOutput, aOutputMaxLen, "receive\r\n"); + Output("receive\r\n"); break; case OT_RADIO_STATE_TRANSMIT: - snprintf(aOutput, aOutputMaxLen, "transmit\r\n"); + Output("transmit\r\n"); break; default: - snprintf(aOutput, aOutputMaxLen, "invalid\r\n"); + Output("invalid\r\n"); break; } } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } @@ -554,7 +600,7 @@ void Diags::TransmitDone(Error aError) #endif // OPENTHREAD_RADIO -Error Diags::ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorInvalidArgs; @@ -571,11 +617,11 @@ Error Diags::ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[], char *aOu } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessStream(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessStream(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorInvalidArgs; @@ -592,7 +638,7 @@ Error Diags::ProcessStream(uint8_t aArgsLength, char *aArgs[], char *aOutput, si } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } @@ -604,7 +650,7 @@ Error Diags::GetPowerSettings(uint8_t aChannel, PowerSettings &aPowerSettings) &aPowerSettings.mRawPowerSetting.mLength); } -Error Diags::ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorInvalidArgs; uint8_t channel; @@ -617,14 +663,9 @@ Error Diags::ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[], char *aOut bool isPrePowerSettingsValid = false; uint8_t preChannel = 0; PowerSettings prePowerSettings; - int n; - n = snprintf(aOutput, aOutputMaxLen, - "| StartCh | EndCh | TargetPower | ActualPower | RawPowerSetting |\r\n" - "+---------+-------+-------------+-------------+-----------------+\r\n"); - VerifyOrExit((n > 0) && (n < static_cast(aOutputMaxLen)), error = kErrorNoBufs); - aOutput += n; - aOutputMaxLen -= static_cast(n); + Output("| StartCh | EndCh | TargetPower | ActualPower | RawPowerSetting |\r\n" + "+---------+-------+-------------+-------------+-----------------+\r\n"); for (channel = Radio::kChannelMin; channel <= Radio::kChannelMax + 1; channel++) { @@ -632,12 +673,8 @@ Error Diags::ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[], char *aOut if (isPrePowerSettingsValid && ((powerSettings != prePowerSettings) || (error != kErrorNone))) { - n = snprintf(aOutput, aOutputMaxLen, "| %7u | %5u | %11d | %11d | %15s |\r\n", preChannel, channel - 1, - prePowerSettings.mTargetPower, prePowerSettings.mActualPower, - prePowerSettings.mRawPowerSetting.ToString().AsCString()); - VerifyOrExit((n > 0) && (n < static_cast(aOutputMaxLen)), error = kErrorNoBufs); - aOutput += n; - aOutputMaxLen -= static_cast(n); + Output("| %7u | %5u | %11d | %11d | %15s |\r\n", preChannel, channel - 1, prePowerSettings.mTargetPower, + prePowerSettings.mActualPower, prePowerSettings.mRawPowerSetting.ToString().AsCString()); isPrePowerSettingsValid = false; } @@ -657,14 +694,13 @@ Error Diags::ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[], char *aOut VerifyOrExit(channel >= Radio::kChannelMin && channel <= Radio::kChannelMax, error = kErrorInvalidArgs); SuccessOrExit(error = GetPowerSettings(channel, powerSettings)); - snprintf(aOutput, aOutputMaxLen, - "TargetPower(0.01dBm): %d\r\nActualPower(0.01dBm): %d\r\nRawPowerSetting: %s\r\n", - powerSettings.mTargetPower, powerSettings.mActualPower, - powerSettings.mRawPowerSetting.ToString().AsCString()); + Output("TargetPower(0.01dBm): %d\r\nActualPower(0.01dBm): %d\r\nRawPowerSetting: %s\r\n", + powerSettings.mTargetPower, powerSettings.mActualPower, + powerSettings.mRawPowerSetting.ToString().AsCString()); } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } @@ -674,7 +710,7 @@ Error Diags::GetRawPowerSetting(RawPowerSetting &aRawPowerSetting) return otPlatDiagRadioGetRawPowerSetting(&GetInstance(), aRawPowerSetting.mData, &aRawPowerSetting.mLength); } -Error Diags::ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorInvalidArgs; RawPowerSetting setting; @@ -684,7 +720,7 @@ Error Diags::ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[], char *aO if (aArgsLength == 0) { SuccessOrExit(error = GetRawPowerSetting(setting)); - snprintf(aOutput, aOutputMaxLen, "%s\r\n", setting.ToString().AsCString()); + Output("%s\r\n", setting.ToString().AsCString()); } else if (strcmp(aArgs[0], "enable") == 0) { @@ -702,11 +738,11 @@ Error Diags::ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[], char *aO } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorInvalidArgs; long value; @@ -719,7 +755,7 @@ Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size SuccessOrExit(error = ParseLong(aArgs[1], value)); gpio = static_cast(value); SuccessOrExit(error = otPlatDiagGpioGet(gpio, &level)); - snprintf(aOutput, aOutputMaxLen, "%d\r\n", level); + Output("%d\r\n", level); } else if ((aArgsLength == 3) && (strcmp(aArgs[0], "set") == 0)) { @@ -738,11 +774,11 @@ Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size SuccessOrExit(error = otPlatDiagGpioGetMode(gpio, &mode)); if (mode == OT_GPIO_MODE_INPUT) { - snprintf(aOutput, aOutputMaxLen, "in\r\n"); + Output("in\r\n"); } else if (mode == OT_GPIO_MODE_OUTPUT) { - snprintf(aOutput, aOutputMaxLen, "out\r\n"); + Output("out\r\n"); } } else if ((aArgsLength == 3) && (strcmp(aArgs[2], "in") == 0)) @@ -756,15 +792,15 @@ Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size } exit: - AppendErrorResult(error, aOutput, aOutputMaxLen); + AppendErrorResult(error); return error; } -void Diags::AppendErrorResult(Error aError, char *aOutput, size_t aOutputMaxLen) +void Diags::AppendErrorResult(Error aError) { if (aError != kErrorNone) { - snprintf(aOutput, aOutputMaxLen, "failed\r\nstatus %#x\r\n", aError); + Output("failed\r\nstatus %#x\r\n", aError); } } @@ -801,7 +837,7 @@ Error Diags::ParseCmd(char *aString, uint8_t &aArgsLength, char *aArgs[]) return error; } -Error Diags::ProcessLine(const char *aString, char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessLine(const char *aString) { constexpr uint16_t kMaxCommandBuffer = OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE; @@ -820,27 +856,26 @@ Error Diags::ProcessLine(const char *aString, char *aOutput, size_t aOutputMaxLe switch (error) { case kErrorNone: - aOutput[0] = '\0'; // In case there is no output. - error = ProcessCmd(argCount, &args[0], aOutput, aOutputMaxLen); + error = ProcessCmd(argCount, &args[0]); break; case kErrorNoBufs: - snprintf(aOutput, aOutputMaxLen, "failed: command string too long\r\n"); + Output("failed: command string too long\r\n"); break; case kErrorInvalidArgs: - snprintf(aOutput, aOutputMaxLen, "failed: command string contains too many arguments\r\n"); + Output("failed: command string contains too many arguments\r\n"); break; default: - snprintf(aOutput, aOutputMaxLen, "failed to parse command string\r\n"); + Output("failed to parse command string\r\n"); break; } return error; } -Error Diags::ProcessCmd(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessCmd(uint8_t aArgsLength, char *aArgs[]) { Error error = kErrorNone; @@ -856,37 +891,54 @@ Error Diags::ProcessCmd(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_ if (aArgsLength == 0) { - snprintf(aOutput, aOutputMaxLen, "diagnostics mode is %s\r\n", otPlatDiagModeGet() ? "enabled" : "disabled"); + Output("diagnostics mode is %s\r\n", otPlatDiagModeGet() ? "enabled" : "disabled"); ExitNow(); } - else - { - aOutput[0] = '\0'; - } for (const Command &command : sCommands) { if (strcmp(aArgs[0], command.mName) == 0) { - error = (this->*command.mCommand)(aArgsLength - 1, (aArgsLength > 1) ? &aArgs[1] : nullptr, aOutput, - aOutputMaxLen); + error = (this->*command.mCommand)(aArgsLength - 1, (aArgsLength > 1) ? &aArgs[1] : nullptr); ExitNow(); } } // more platform specific features will be processed under platform layer - error = otPlatDiagProcess(&GetInstance(), aArgsLength, aArgs, aOutput, aOutputMaxLen); + error = otPlatDiagProcess(&GetInstance(), aArgsLength, aArgs); exit: // Add more platform specific diagnostics features here. if (error == kErrorInvalidCommand && aArgsLength > 1) { - snprintf(aOutput, aOutputMaxLen, "diag feature '%s' is not supported\r\n", aArgs[0]); + Output("diag feature '%s' is not supported\r\n", aArgs[0]); } return error; } +void Diags::SetOutputCallback(otDiagOutputCallback aCallback, void *aContext) +{ + mOutputCallback = aCallback; + mOutputContext = aContext; + + otPlatDiagSetOutputCallback(&GetInstance(), aCallback, aContext); +} + +void Diags::Output(const char *aFormat, ...) +{ + va_list args; + + va_start(args, aFormat); + + if (mOutputCallback != nullptr) + { + mOutputCallback(aFormat, args, mOutputContext); + } + + va_end(args); +} + bool Diags::IsEnabled(void) { return otPlatDiagModeGet(); } } // namespace FactoryDiags @@ -986,4 +1038,5 @@ OT_TOOL_WEAK otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance, return OT_ERROR_NOT_IMPLEMENTED; } + #endif // OPENTHREAD_CONFIG_DIAG_ENABLE diff --git a/src/core/diags/factory_diags.hpp b/src/core/diags/factory_diags.hpp index 1b72a1563..01864786d 100644 --- a/src/core/diags/factory_diags.hpp +++ b/src/core/diags/factory_diags.hpp @@ -40,6 +40,7 @@ #include +#include #include #include "common/clearable.hpp" @@ -66,26 +67,22 @@ class Diags : public InstanceLocator, private NonCopyable * Processes a factory diagnostics command line. * * @param[in] aString A null-terminated input string. - * @param[out] aOutput The diagnostics execution result. - * @param[in] aOutputMaxLen The output buffer size. * */ - Error ProcessLine(const char *aString, char *aOutput, size_t aOutputMaxLen); + Error ProcessLine(const char *aString); /** * Processes a factory diagnostics command line. * * @param[in] aArgsLength The number of args in @p aArgs. * @param[in] aArgs The arguments of diagnostics command line. - * @param[out] aOutput The diagnostics execution result. - * @param[in] aOutputMaxLen The output buffer size. * * @retval kErrorInvalidArgs The command is supported but invalid arguments provided. * @retval kErrorNone The command is successfully process. * @retval kErrorNotImplemented The command is not supported. * */ - Error ProcessCmd(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error ProcessCmd(uint8_t aArgsLength, char *aArgs[]); /** * Indicates whether or not the factory diagnostics mode is enabled. @@ -123,13 +120,22 @@ class Diags : public InstanceLocator, private NonCopyable */ void TransmitDone(Error aError); + /** + * Sets the diag output callback. + * + * @param[in] aCallback A callback method called to output diag messages. + * @param[in] aContext A user context pointer. + * + */ + void SetOutputCallback(otDiagOutputCallback aCallback, void *aContext); + private: static constexpr uint8_t kMaxArgs = OPENTHREAD_CONFIG_DIAG_CMD_LINE_ARGS_MAX; struct Command { const char *mName; - Error (Diags::*mCommand)(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error (Diags::*mCommand)(uint8_t aArgsLength, char *aArgs[]); }; struct Stats : public Clearable @@ -180,29 +186,31 @@ class Diags : public InstanceLocator, private NonCopyable }; Error ParseCmd(char *aString, uint8_t &aArgsLength, char *aArgs[]); - Error ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessGpio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessRadio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessRepeat(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessSend(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessStats(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessStop(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); - Error ProcessStream(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error ProcessChannel(uint8_t aArgsLength, char *aArgs[]); + Error ProcessFrame(uint8_t aArgsLength, char *aArgs[]); + Error ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[]); + Error ProcessGpio(uint8_t aArgsLength, char *aArgs[]); + Error ProcessPower(uint8_t aArgsLength, char *aArgs[]); + Error ProcessRadio(uint8_t aArgsLength, char *aArgs[]); + Error ProcessRepeat(uint8_t aArgsLength, char *aArgs[]); + Error ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[]); + Error ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[]); + Error ProcessSend(uint8_t aArgsLength, char *aArgs[]); + Error ProcessStart(uint8_t aArgsLength, char *aArgs[]); + Error ProcessStats(uint8_t aArgsLength, char *aArgs[]); + Error ProcessStop(uint8_t aArgsLength, char *aArgs[]); + Error ProcessStream(uint8_t aArgsLength, char *aArgs[]); #if OPENTHREAD_RADIO && !OPENTHREAD_RADIO_CLI - Error ProcessEcho(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error ProcessEcho(uint8_t aArgsLength, char *aArgs[]); #endif Error GetRawPowerSetting(RawPowerSetting &aRawPowerSetting); Error GetPowerSettings(uint8_t aChannel, PowerSettings &aPowerSettings); void TransmitPacket(void); + void Output(const char *aFormat, ...); + void AppendErrorResult(Error aError); - static void AppendErrorResult(Error aError, char *aOutput, size_t aOutputMaxLen); static Error ParseLong(char *aString, long &aLong); static Error ParseBool(char *aString, bool &aBool); @@ -217,9 +225,13 @@ class Diags : public InstanceLocator, private NonCopyable uint8_t mChannel; int8_t mTxPower; uint8_t mTxLen; + bool mIsTxPacketSet; bool mRepeatActive; bool mDiagSendOn; #endif + + otDiagOutputCallback mOutputCallback; + void *mOutputContext; }; } // namespace FactoryDiags diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index e70fc982b..d1fbd0a8e 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -340,7 +340,7 @@ void Mac::PerformEnergyScan(void) } else { - if (!GetRxOnWhenIdle()) + if (!mRxOnWhenIdle) { mLinks.Receive(mScanChannel); } @@ -1316,7 +1316,7 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError) ProcessCsl(*aAckFrame, dstAddr); #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - if (!GetRxOnWhenIdle() && aFrame.GetHeaderIe(CslIe::kHeaderIeId) != nullptr) + if (!mRxOnWhenIdle && aFrame.GetHeaderIe(CslIe::kHeaderIeId) != nullptr) { Get().ResetKeepAliveTimer(); } @@ -1641,7 +1641,7 @@ Error Mac::ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Neig if (keySequence > keyManager.GetCurrentKeySequence()) { - keyManager.SetCurrentKeySequence(keySequence, KeyManager::kApplyKeySwitchGuard); + keyManager.SetCurrentKeySequence(keySequence, KeyManager::kApplySwitchGuard | KeyManager::kResetGuardTimer); } } diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index 24e7331d8..ef82c3965 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -200,7 +200,7 @@ void BorderAgent::HandleCoapResponse(const ForwardContext &aForwardContext, SuccessOrExit(error = Tlv::Find(*aResponse, sessionId)); - IgnoreError(Get().GetCommissionerAloc(mCommissionerAloc.GetAddress(), sessionId)); + Get().GetCommissionerAloc(sessionId, mCommissionerAloc.GetAddress()); Get().AddUnicastAddress(mCommissionerAloc); IgnoreError(Get().AddReceiver(mUdpReceiver)); @@ -561,7 +561,7 @@ Error BorderAgent::ForwardToLeader(const Coap::Message &aMessage, const Ip6::Mes SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, aMessage.GetOffset(), aMessage.GetLength() - aMessage.GetOffset())); - SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); + messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc(); messageInfo.SetSockPortToTmf(); SuccessOrExit(error = diff --git a/src/core/meshcop/commissioner.cpp b/src/core/meshcop/commissioner.cpp index bb09ebb24..c3987e82c 100644 --- a/src/core/meshcop/commissioner.cpp +++ b/src/core/meshcop/commissioner.cpp @@ -639,7 +639,7 @@ Error Commissioner::SendMgmtCommissionerGetRequest(const uint8_t *aTlvs, uint8_t SuccessOrExit(error = message->AppendBytes(aTlvs, aLength)); } - SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); + messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc(); SuccessOrExit(error = Get().SendMessage(*message, messageInfo, Commissioner::HandleMgmtCommissionerGetResponse, this)); @@ -710,7 +710,7 @@ Error Commissioner::SendMgmtCommissionerSetRequest(const CommissioningDataset &a SuccessOrExit(error = message->AppendBytes(aTlvs, aLength)); } - SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); + messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc(); SuccessOrExit(error = Get().SendMessage(*message, messageInfo, Commissioner::HandleMgmtCommissionerSetResponse, this)); @@ -763,7 +763,7 @@ Error Commissioner::SendPetition(void) SuccessOrExit(error = Tlv::Append(*message, mCommissionerId)); - SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); + messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc(); SuccessOrExit( error = Get().SendMessage(*message, messageInfo, Commissioner::HandleLeaderPetitionResponse, this)); @@ -811,7 +811,7 @@ void Commissioner::HandleLeaderPetitionResponse(Coap::Message *aMessage ExitNow(); } - IgnoreError(Get().GetCommissionerAloc(mCommissionerAloc.GetAddress(), mSessionId)); + Get().GetCommissionerAloc(mSessionId, mCommissionerAloc.GetAddress()); Get().AddUnicastAddress(mCommissionerAloc); SetState(kStateActive); @@ -850,7 +850,7 @@ void Commissioner::SendKeepAlive(uint16_t aSessionId) SuccessOrExit(error = Tlv::Append(*message, aSessionId)); - SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); + messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc(); SuccessOrExit(error = Get().SendMessage(*message, messageInfo, Commissioner::HandleLeaderKeepAliveResponse, this)); @@ -954,7 +954,7 @@ template <> void Commissioner::HandleTmf(Coap::Message &aMessage, c aMessage.SetOffset(startOffset); SuccessOrExit(error = aMessage.SetLength(endOffset)); - joinerMessageInfo.SetPeerAddr(Get().GetMeshLocal64()); + joinerMessageInfo.SetPeerAddr(Get().GetMeshLocalEid()); joinerMessageInfo.GetPeerAddr().SetIid(mJoinerIid); joinerMessageInfo.SetPeerPort(mJoinerPort); @@ -1048,7 +1048,7 @@ void Commissioner::SendJoinFinalizeResponse(const Coap::Message &aRequest, State SuccessOrExit(error = Tlv::Append(*message, aState)); - joinerMessageInfo.SetPeerAddr(Get().GetMeshLocal64()); + joinerMessageInfo.SetPeerAddr(Get().GetMeshLocalEid()); joinerMessageInfo.GetPeerAddr().SetIid(mJoinerIid); joinerMessageInfo.SetPeerPort(mJoinerPort); diff --git a/src/core/meshcop/dataset.cpp b/src/core/meshcop/dataset.cpp index 8f2202323..cb0c53e9a 100644 --- a/src/core/meshcop/dataset.cpp +++ b/src/core/meshcop/dataset.cpp @@ -576,45 +576,5 @@ bool Dataset::IsSubsetOf(const Dataset &aOther) const 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 165999d93..d458b2c78 100644 --- a/src/core/meshcop/dataset.hpp +++ b/src/core/meshcop/dataset.hpp @@ -702,35 +702,6 @@ 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); diff --git a/src/core/meshcop/dataset_manager.cpp b/src/core/meshcop/dataset_manager.cpp index ec6e89077..8038af1fb 100644 --- a/src/core/meshcop/dataset_manager.cpp +++ b/src/core/meshcop/dataset_manager.cpp @@ -267,22 +267,6 @@ void DatasetManager::Clear(void) SignalDatasetChange(); } -Error DatasetManager::Save(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint16_t aLength) -{ - Error error = kErrorNone; - Dataset dataset; - - SuccessOrExit(error = dataset.SetFrom(aMessage, aOffset, aLength)); - SuccessOrExit(error = dataset.ValidateTlvs()); - - SuccessOrExit(error = dataset.WriteTimestamp(mType, aTimestamp)); - - error = Save(dataset); - -exit: - return error; -} - Error DatasetManager::Save(const Dataset &aDataset, bool aAllowOlderTimestamp) { Error error = kErrorNone; @@ -499,7 +483,7 @@ Error DatasetManager::SendSetRequest(const Dataset &aDataset) VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->AppendBytes(aDataset.GetBytes(), aDataset.GetLength())); - IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); + messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc(); SuccessOrExit(error = Get().SendMessage(*message, messageInfo, HandleMgmtSetResponse, this)); mMgmtPending = true; @@ -724,7 +708,7 @@ Error DatasetManager::SendGetRequest(const Dataset::Components &aDatasetComponen SuccessOrExit(error = Tlv::AppendTlv(*message, Tlv::kGet, tlvList.GetArrayBuffer(), tlvList.GetLength())); } - IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); + messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc(); if (aAddress != nullptr) { @@ -776,7 +760,7 @@ void DatasetManager::MoveKeysToSecureStorage(Dataset &aDataset) const { for (const SecurelyStoredTlv &entry : kSecurelyStoredTlvs) { - aDataset.SaveTlvInSecureStorageAndClearValue(entry.mTlvType, entry.GetKeyRef(mType)); + SaveTlvInSecureStorageAndClearValue(aDataset, entry.mTlvType, entry.GetKeyRef(mType)); } } @@ -791,7 +775,7 @@ void DatasetManager::EmplaceSecurelyStoredKeys(Dataset &aDataset) const for (const SecurelyStoredTlv &entry : kSecurelyStoredTlvs) { - if (aDataset.ReadTlvFromSecureStorage(entry.mTlvType, entry.GetKeyRef(mType)) != kErrorNone) + if (ReadTlvFromSecureStorage(aDataset, entry.mTlvType, entry.GetKeyRef(mType)) != kErrorNone) { moveKeys = true; } @@ -807,6 +791,42 @@ void DatasetManager::EmplaceSecurelyStoredKeys(Dataset &aDataset) const } } +void DatasetManager::SaveTlvInSecureStorageAndClearValue(Dataset &aDataset, Tlv::Type aTlvType, KeyRef aKeyRef) const +{ + using namespace ot::Crypto::Storage; + + Tlv *tlv = aDataset.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 DatasetManager::ReadTlvFromSecureStorage(Dataset &aDataset, Tlv::Type aTlvType, KeyRef aKeyRef) const +{ + using namespace ot::Crypto::Storage; + + Error error = kErrorNone; + Tlv *tlv = aDataset.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 //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/core/meshcop/dataset_manager.hpp b/src/core/meshcop/dataset_manager.hpp index 87c9544e6..270903b8d 100644 --- a/src/core/meshcop/dataset_manager.hpp +++ b/src/core/meshcop/dataset_manager.hpp @@ -162,23 +162,6 @@ class DatasetManager : public InstanceLocator */ Error Save(const Dataset &aDataset) { return Save(aDataset, /* aAllowOlderTimestamp */ false); } - /** - * Sets the Operational Dataset for the partition read from a given message. - * - * Also updates the non-volatile local version if the partition's Operational Dataset is newer. If Active - * Operational Dataset is changed, applies the configuration to to Thread interface. - * - * @param[in] aTimestamp The timestamp for the Operational Dataset. - * @param[in] aMessage The message to read from. - * @param[in] aOffset The offset where the Operational Dataset begins. - * @param[in] aLength The length of the Operational Dataset. - * - * @retval kErrorNone Successfully parsed the Dataset from the @p aMessage and saved it. - * @retval kErrorParse Could not parse the Dataset from @p aMessage. - * - */ - Error Save(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint16_t aLength); - /** * Retrieves the channel mask from local dataset. * @@ -250,16 +233,18 @@ class DatasetManager : public InstanceLocator }; #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + using KeyRef = Crypto::Storage::KeyRef; + struct SecurelyStoredTlv { - Crypto::Storage::KeyRef GetKeyRef(Dataset::Type aType) const + KeyRef GetKeyRef(Dataset::Type aType) const { return (aType == Dataset::kActive) ? mActiveKeyRef : mPendingKeyRef; } - Tlv::Type mTlvType; - Crypto::Storage::KeyRef mActiveKeyRef; - Crypto::Storage::KeyRef mPendingKeyRef; + Tlv::Type mTlvType; + KeyRef mActiveKeyRef; + KeyRef mPendingKeyRef; }; static const SecurelyStoredTlv kSecurelyStoredTlvs[]; @@ -305,9 +290,11 @@ class DatasetManager : public InstanceLocator Error aError); #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE - void MoveKeysToSecureStorage(Dataset &aDataset) const; - void DestroySecurelyStoredKeys(void) const; - void EmplaceSecurelyStoredKeys(Dataset &aDataset) const; + void MoveKeysToSecureStorage(Dataset &aDataset) const; + void DestroySecurelyStoredKeys(void) const; + void EmplaceSecurelyStoredKeys(Dataset &aDataset) const; + void SaveTlvInSecureStorageAndClearValue(Dataset &aDataset, Tlv::Type aTlvType, KeyRef aKeyRef) const; + Error ReadTlvFromSecureStorage(Dataset &aDataset, Tlv::Type aTlvType, KeyRef aKeyRef) const; #endif #if OPENTHREAD_FTD diff --git a/src/core/meshcop/dataset_manager_ftd.cpp b/src/core/meshcop/dataset_manager_ftd.cpp index 927d24543..956a5fa58 100644 --- a/src/core/meshcop/dataset_manager_ftd.cpp +++ b/src/core/meshcop/dataset_manager_ftd.cpp @@ -241,7 +241,7 @@ Error DatasetManager::HandleSetOrReplace(MgmtCommand aCommand, Ip6::Address destination; SuccessOrExit(Get().FindCommissioningSessionId(localSessionId)); - SuccessOrExit(Get().GetCommissionerAloc(destination, localSessionId)); + Get().GetCommissionerAloc(localSessionId, destination); Get().SendDatasetChanged(destination); } diff --git a/src/core/meshcop/joiner_router.cpp b/src/core/meshcop/joiner_router.cpp index a5df41367..b9f4a57c4 100644 --- a/src/core/meshcop/joiner_router.cpp +++ b/src/core/meshcop/joiner_router.cpp @@ -56,7 +56,7 @@ RegisterLogModule("JoinerRouter"); JoinerRouter::JoinerRouter(Instance &aInstance) : InstanceLocator(aInstance) - , mSocket(aInstance) + , mSocket(aInstance, *this) , mTimer(aInstance) , mJoinerUdpPort(0) , mIsJoinerPortConfigured(false) @@ -81,7 +81,7 @@ void JoinerRouter::Start(void) VerifyOrExit(!mSocket.IsBound()); - IgnoreError(mSocket.Open(&JoinerRouter::HandleUdpReceive, this)); + IgnoreError(mSocket.Open()); IgnoreError(mSocket.Bind(port)); IgnoreError(Get().AddUnsecurePort(port)); LogInfo("Joiner Router: start"); @@ -126,11 +126,6 @@ void JoinerRouter::SetJoinerUdpPort(uint16_t aJoinerUdpPort) Start(); } -void JoinerRouter::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo)); -} - void JoinerRouter::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error; diff --git a/src/core/meshcop/joiner_router.hpp b/src/core/meshcop/joiner_router.hpp index bc76297ba..63672e2a5 100644 --- a/src/core/meshcop/joiner_router.hpp +++ b/src/core/meshcop/joiner_router.hpp @@ -100,8 +100,7 @@ class JoinerRouter : public InstanceLocator, private NonCopyable void HandleNotifierEvents(Events aEvents); - static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); @@ -120,8 +119,9 @@ class JoinerRouter : public InstanceLocator, private NonCopyable Coap::Message *PrepareJoinerEntrustMessage(void); using JoinerRouterTimer = TimerMilliIn; + using JoinerSocket = Ip6::Udp::SocketIn; - Ip6::Udp::Socket mSocket; + JoinerSocket mSocket; JoinerRouterTimer mTimer; MessageQueue mDelayedJoinEnts; diff --git a/src/core/meshcop/meshcop_tlvs.hpp b/src/core/meshcop/meshcop_tlvs.hpp index 084f84d8b..e35abf834 100644 --- a/src/core/meshcop/meshcop_tlvs.hpp +++ b/src/core/meshcop/meshcop_tlvs.hpp @@ -270,7 +270,7 @@ typedef SimpleTlvInfo ExtendedPanIdTlv; * */ OT_TOOL_PACKED_BEGIN -class NetworkNameTlv : public Tlv, public TlvInfo +class NetworkNameTlv : public Tlv, public StringTlvInfo { public: /** diff --git a/src/core/meshcop/secure_transport.cpp b/src/core/meshcop/secure_transport.cpp index e527c1f1e..bfb5db6d9 100644 --- a/src/core/meshcop/secure_transport.cpp +++ b/src/core/meshcop/secure_transport.cpp @@ -87,7 +87,7 @@ SecureTransport::SecureTransport(Instance &aInstance, bool aLayerTwoSecurity, bo , mMaxConnectionAttempts(0) , mRemainingConnectionAttempts(0) , mReceiveMessage(nullptr) - , mSocket(aInstance) + , mSocket(aInstance, *this) , mMessageSubType(Message::kSubTypeNone) , mMessageDefaultSubType(Message::kSubTypeNone) { @@ -158,7 +158,7 @@ Error SecureTransport::Open(ReceiveHandler aReceiveHandler, ConnectedHandler aCo VerifyOrExit(IsStateClosed(), error = kErrorAlready); - SuccessOrExit(error = mSocket.Open(&SecureTransport::HandleReceive, this)); + SuccessOrExit(error = mSocket.Open()); mConnectedCallback.Set(aConnectedHandler, aContext); mReceiveCallback.Set(aReceiveHandler, aContext); @@ -204,11 +204,6 @@ Error SecureTransport::Connect(const Ip6::SockAddr &aSockAddr) return error; } -void SecureTransport::HandleReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo)); -} - void SecureTransport::HandleReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { VerifyOrExit(!IsStateClosed()); diff --git a/src/core/meshcop/secure_transport.hpp b/src/core/meshcop/secure_transport.hpp index 4641ef537..b5749a951 100644 --- a/src/core/meshcop/secure_transport.hpp +++ b/src/core/meshcop/secure_transport.hpp @@ -584,8 +584,6 @@ class SecureTransport : public InstanceLocator static void HandleTimer(Timer &aTimer); void HandleTimer(void); - static void HandleReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleReceive(const uint8_t *aBuf, uint16_t aLength); Error HandleSecureTransportSend(const uint8_t *aBuf, uint16_t aLength, Message::SubType aMessageSubType); @@ -595,6 +593,8 @@ class SecureTransport : public InstanceLocator static const char *StateToString(State aState); #endif + using TransportSocket = Ip6::Udp::SocketIn; + State mState; int mCipherSuites[2]; @@ -663,7 +663,7 @@ class SecureTransport : public InstanceLocator void *mContext; Ip6::MessageInfo mMessageInfo; - Ip6::Udp::Socket mSocket; + TransportSocket mSocket; Callback mTransportCallback; void *mTransportContext; diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp index 29ae911b0..48dcba714 100644 --- a/src/core/meshcop/tcat_agent.cpp +++ b/src/core/meshcop/tcat_agent.cpp @@ -460,9 +460,8 @@ Error TcatAgent::HandleSingleTlv(const Message &aIncommingMessage, Message &aOut Error TcatAgent::HandleSetActiveOperationalDataset(const Message &aIncommingMessage, uint16_t aOffset, uint16_t aLength) { - Dataset dataset; - Dataset::Tlvs datasetTlvs; - Error error; + Dataset dataset; + Error error; SuccessOrExit(error = dataset.SetFrom(aIncommingMessage, aOffset, aLength)); SuccessOrExit(error = dataset.ValidateTlvs()); @@ -474,8 +473,7 @@ Error TcatAgent::HandleSetActiveOperationalDataset(const Message &aIncommingMess ExitNow(); } - dataset.ConvertTo(datasetTlvs); - error = Get().SaveLocal(datasetTlvs); + Get().SaveLocal(dataset); exit: return error; diff --git a/src/core/net/dhcp6_client.cpp b/src/core/net/dhcp6_client.cpp index 56654fb47..abf6e3265 100644 --- a/src/core/net/dhcp6_client.cpp +++ b/src/core/net/dhcp6_client.cpp @@ -52,7 +52,7 @@ RegisterLogModule("Dhcp6Client"); Client::Client(Instance &aInstance) : InstanceLocator(aInstance) - , mSocket(aInstance) + , mSocket(aInstance, *this) , mTrickleTimer(aInstance, Client::HandleTrickleTimer) , mStartTime(0) , mIdentityAssociationCurrent(nullptr) @@ -170,7 +170,7 @@ void Client::Start(void) { VerifyOrExit(!mSocket.IsBound()); - IgnoreError(mSocket.Open(&Client::HandleUdpReceive, this)); + IgnoreError(mSocket.Open()); IgnoreError(mSocket.Bind(kDhcpClientPort)); ProcessNextIdentityAssociation(); @@ -278,7 +278,7 @@ void Client::Solicit(uint16_t aRloc16) #else messageInfo.GetPeerAddr().SetToRoutingLocator(Get().GetMeshLocalPrefix(), aRloc16); #endif - messageInfo.SetSockAddr(Get().GetMeshLocal16()); + messageInfo.SetSockAddr(Get().GetMeshLocalRloc()); messageInfo.mPeerPort = kDhcpServerPort; SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); @@ -395,11 +395,6 @@ Error Client::AppendRapidCommit(Message &aMessage) return aMessage.Append(option); } -void Client::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo)); -} - void Client::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { OT_UNUSED_VARIABLE(aMessageInfo); diff --git a/src/core/net/dhcp6_client.hpp b/src/core/net/dhcp6_client.hpp index c50b13889..2d9287db0 100644 --- a/src/core/net/dhcp6_client.hpp +++ b/src/core/net/dhcp6_client.hpp @@ -126,8 +126,7 @@ class Client : public InstanceLocator, private NonCopyable Error AppendElapsedTime(Message &aMessage); Error AppendRapidCommit(Message &aMessage); - static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void ProcessReply(Message &aMessage); uint16_t FindOption(Message &aMessage, uint16_t aOffset, uint16_t aLength, Code aCode); @@ -140,8 +139,9 @@ class Client : public InstanceLocator, private NonCopyable static void HandleTrickleTimer(TrickleTimer &aTrickleTimer); void HandleTrickleTimer(void); - Ip6::Udp::Socket mSocket; + using ClientSocket = Ip6::Udp::SocketIn; + ClientSocket mSocket; TrickleTimer mTrickleTimer; TransactionId mTransactionId; diff --git a/src/core/net/dhcp6_server.cpp b/src/core/net/dhcp6_server.cpp index ea9570737..f25884e12 100644 --- a/src/core/net/dhcp6_server.cpp +++ b/src/core/net/dhcp6_server.cpp @@ -52,7 +52,7 @@ RegisterLogModule("Dhcp6Server"); Server::Server(Instance &aInstance) : InstanceLocator(aInstance) - , mSocket(aInstance) + , mSocket(aInstance, *this) , mPrefixAgentsCount(0) , mPrefixAgentsMask(0) { @@ -138,7 +138,7 @@ void Server::Start(void) { VerifyOrExit(!mSocket.IsOpen()); - IgnoreError(mSocket.Open(&Server::HandleUdpReceive, this)); + IgnoreError(mSocket.Open()); IgnoreError(mSocket.Bind(kDhcpServerPort)); exit: @@ -176,11 +176,6 @@ void Server::AddPrefixAgent(const Ip6::Prefix &aIp6Prefix, const Lowpan::Context OT_UNUSED_VARIABLE(error); } -void Server::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo)); -} - void Server::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Header header; diff --git a/src/core/net/dhcp6_server.hpp b/src/core/net/dhcp6_server.hpp index eba2d6c96..8869f24e6 100644 --- a/src/core/net/dhcp6_server.hpp +++ b/src/core/net/dhcp6_server.hpp @@ -192,11 +192,8 @@ class Server : public InstanceLocator, private NonCopyable Error AppendVendorSpecificInformation(Message &aMessage); Error AddIaAddress(Message &aMessage, const Ip6::Address &aPrefix, ClientIdentifier &aClientId); - - static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - - void ProcessSolicit(Message &aMessage, const Ip6::Address &aDst, const TransactionId &aTransactionId); + void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + void ProcessSolicit(Message &aMessage, const Ip6::Address &aDst, const TransactionId &aTransactionId); uint16_t FindOption(Message &aMessage, uint16_t aOffset, uint16_t aLength, Code aCode); Error ProcessClientIdentifier(Message &aMessage, uint16_t aOffset, ClientIdentifier &aClientId); @@ -209,7 +206,9 @@ class Server : public InstanceLocator, private NonCopyable ClientIdentifier &aClientId, IaNa &aIaNa); - Ip6::Udp::Socket mSocket; + using ServerSocket = Ip6::Udp::SocketIn; + + ServerSocket mSocket; PrefixAgent mPrefixAgents[kNumPrefixes]; uint8_t mPrefixAgentsCount; diff --git a/src/core/net/dns_client.cpp b/src/core/net/dns_client.cpp index 61bdb1714..49a0d4d5a 100644 --- a/src/core/net/dns_client.cpp +++ b/src/core/net/dns_client.cpp @@ -737,7 +737,7 @@ const uint16_t *const Client::kQuestionRecordTypes[] = { Client::Client(Instance &aInstance) : InstanceLocator(aInstance) - , mSocket(aInstance) + , mSocket(aInstance, *this) #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE , mTcpState(kTcpUninitialized) #endif @@ -768,7 +768,7 @@ Error Client::Start(void) { Error error; - SuccessOrExit(error = mSocket.Open(&Client::HandleUdpReceive, this)); + SuccessOrExit(error = mSocket.Open()); SuccessOrExit(error = mSocket.Bind(0, Ip6::kNetifUnspecified)); exit: @@ -1301,11 +1301,10 @@ Client::Query *Client::FindQueryById(uint16_t aMessageId) return matchedQuery; } -void Client::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMsgInfo) +void Client::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMsgInfo) { OT_UNUSED_VARIABLE(aMsgInfo); - - static_cast(aContext)->ProcessResponse(AsCoreType(aMessage)); + ProcessResponse(aMessage); } void Client::ProcessResponse(const Message &aResponseMessage) @@ -1521,7 +1520,17 @@ void Client::HandleTimer(void) break; } - IgnoreError(SendQuery(*query, info, /* aUpdateTimer */ false)); +#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE + if (ReplaceWithSeparateSrvTxtQueries(*query) == kErrorNone) + { + LogInfo("Switching to separate SRV/TXT on response timeout"); + info.ReadFrom(*query); + } + else +#endif + { + IgnoreError(SendQuery(*query, info, /* aUpdateTimer */ false)); + } } nextTime.UpdateIfEarlier(info.mRetransmissionTime); @@ -1535,7 +1544,7 @@ void Client::HandleTimer(void) } } - mTimer.FireAt(nextTime); + mTimer.FireAtIfEarlier(nextTime); #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE if (!hasTcpQuery && mTcpState != kTcpUninitialized) diff --git a/src/core/net/dns_client.hpp b/src/core/net/dns_client.hpp index e086f746b..9ac6a3868 100644 --- a/src/core/net/dns_client.hpp +++ b/src/core/net/dns_client.hpp @@ -839,7 +839,7 @@ class Client : public InstanceLocator, private NonCopyable static void GetQueryTypeAndCallback(const Query &aQuery, QueryType &aType, Callback &aCallback, void *&aContext); Error AppendNameFromQuery(const Query &aQuery, Message &aMessage); Query *FindQueryById(uint16_t aMessageId); - static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMsgInfo); + void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMsgInfo); void ProcessResponse(const Message &aResponseMessage); Error ParseResponse(const Message &aResponseMessage, Query *&aQuery, Error &aResponseError); bool CanFinalizeQuery(Query &aQuery); @@ -904,9 +904,10 @@ class Client : public InstanceLocator, private NonCopyable static constexpr uint16_t kUdpQueryMaxSize = 512; - using RetryTimer = TimerMilliIn; + using RetryTimer = TimerMilliIn; + using ClientSocket = Ip6::Udp::SocketIn; - Ip6::Udp::Socket mSocket; + ClientSocket mSocket; #if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE Ip6::Tcp::Endpoint mEndpoint; diff --git a/src/core/net/dnssd.cpp b/src/core/net/dnssd.cpp index 5e068b906..6d54b5e99 100644 --- a/src/core/net/dnssd.cpp +++ b/src/core/net/dnssd.cpp @@ -255,29 +255,255 @@ void Dnssd::UnregisterKey(const Key &aKey, RequestId aRequestId, RegisterCallbac return; } +void Dnssd::StartBrowser(const Browser &aBrowser) +{ + VerifyOrExit(IsReady()); + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ALLOW_RUN_TIME_SELECTION + if (mUseNativeMdns) +#endif #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE -void Dnssd::HandleMdnsCoreStateChange(void) + { + IgnoreError(Get().StartBrowser(aBrowser)); + ExitNow(); + } +#endif + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE + otPlatDnssdStartBrowser(&GetInstance(), &aBrowser); +#endif + +exit: + return; +} + +void Dnssd::StopBrowser(const Browser &aBrowser) { + VerifyOrExit(IsReady()); + #if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ALLOW_RUN_TIME_SELECTION if (mUseNativeMdns) #endif +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE { - HandleStateChange(); + IgnoreError(Get().StopBrowser(aBrowser)); + ExitNow(); + } +#endif + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE + otPlatDnssdStopBrowser(&GetInstance(), &aBrowser); +#endif + +exit: + return; +} + +void Dnssd::StartSrvResolver(const SrvResolver &aResolver) +{ + VerifyOrExit(IsReady()); + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ALLOW_RUN_TIME_SELECTION + if (mUseNativeMdns) +#endif +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE + { + IgnoreError(Get().StartSrvResolver(aResolver)); + ExitNow(); + } +#endif + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE + otPlatDnssdStartSrvResolver(&GetInstance(), &aResolver); +#endif + +exit: + return; +} + +void Dnssd::StopSrvResolver(const SrvResolver &aResolver) +{ + VerifyOrExit(IsReady()); + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ALLOW_RUN_TIME_SELECTION + if (mUseNativeMdns) +#endif +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE + { + IgnoreError(Get().StopSrvResolver(aResolver)); + ExitNow(); + } +#endif + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE + otPlatDnssdStopSrvResolver(&GetInstance(), &aResolver); +#endif + +exit: + return; +} + +void Dnssd::StartTxtResolver(const TxtResolver &aResolver) +{ + VerifyOrExit(IsReady()); + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ALLOW_RUN_TIME_SELECTION + if (mUseNativeMdns) +#endif +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE + { + IgnoreError(Get().StartTxtResolver(aResolver)); + ExitNow(); + } +#endif + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE + otPlatDnssdStartTxtResolver(&GetInstance(), &aResolver); +#endif + +exit: + return; +} + +void Dnssd::StopTxtResolver(const TxtResolver &aResolver) +{ + VerifyOrExit(IsReady()); + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ALLOW_RUN_TIME_SELECTION + if (mUseNativeMdns) +#endif +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE + { + IgnoreError(Get().StopTxtResolver(aResolver)); + ExitNow(); } +#endif + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE + otPlatDnssdStopTxtResolver(&GetInstance(), &aResolver); +#endif + +exit: + return; +} + +void Dnssd::StartIp6AddressResolver(const AddressResolver &aResolver) +{ + VerifyOrExit(IsReady()); + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ALLOW_RUN_TIME_SELECTION + if (mUseNativeMdns) +#endif +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE + { + IgnoreError(Get().StartIp6AddressResolver(aResolver)); + ExitNow(); + } +#endif + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE + otPlatDnssdStartIp6AddressResolver(&GetInstance(), &aResolver); +#endif + +exit: + return; } + +void Dnssd::StopIp6AddressResolver(const AddressResolver &aResolver) +{ + VerifyOrExit(IsReady()); + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ALLOW_RUN_TIME_SELECTION + if (mUseNativeMdns) +#endif +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE + { + IgnoreError(Get().StopIp6AddressResolver(aResolver)); + ExitNow(); + } #endif +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE + otPlatDnssdStopIp6AddressResolver(&GetInstance(), &aResolver); +#endif + +exit: + return; +} + +void Dnssd::StartIp4AddressResolver(const AddressResolver &aResolver) +{ + VerifyOrExit(IsReady()); + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ALLOW_RUN_TIME_SELECTION + if (mUseNativeMdns) +#endif +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE + { + IgnoreError(Get().StartIp4AddressResolver(aResolver)); + ExitNow(); + } +#endif + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE + otPlatDnssdStartIp4AddressResolver(&GetInstance(), &aResolver); +#endif + +exit: + return; +} + +void Dnssd::StopIp4AddressResolver(const AddressResolver &aResolver) +{ + VerifyOrExit(IsReady()); + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ALLOW_RUN_TIME_SELECTION + if (mUseNativeMdns) +#endif +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE + { + IgnoreError(Get().StopIp4AddressResolver(aResolver)); + ExitNow(); + } +#endif + +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE + otPlatDnssdStopIp4AddressResolver(&GetInstance(), &aResolver); +#endif + +exit: + return; +} + void Dnssd::HandleStateChange(void) { #if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE Get().HandleDnssdPlatformStateChange(); #endif + +#if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE && OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + Get().HandleDnssdPlatformStateChange(); +#endif +} + +#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE +void Dnssd::HandleMdnsCoreStateChange(void) +{ +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ALLOW_RUN_TIME_SELECTION + if (mUseNativeMdns) +#endif + { + HandleStateChange(); + } } +#endif +#if OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE extern "C" void otPlatDnssdStateHandleStateChange(otInstance *aInstance) { AsCoreType(aInstance).Get().HandleStateChange(); } +#endif } // namespace ot diff --git a/src/core/net/dnssd.hpp b/src/core/net/dnssd.hpp index 9bf19aa81..1864a377c 100644 --- a/src/core/net/dnssd.hpp +++ b/src/core/net/dnssd.hpp @@ -93,6 +93,15 @@ class Dnssd : public InstanceLocator, private NonCopyable typedef otPlatDnssdRequestId RequestId; ///< A request ID. typedef otPlatDnssdRegisterCallback RegisterCallback; ///< The registration request callback + typedef otPlatDnssdBrowseCallback BrowseCallback; ///< Browser callback. + typedef otPlatDnssdSrvCallback SrvCallback; ///< SRV callback. + typedef otPlatDnssdTxtCallback TxtCallback; ///< TXT callback. + typedef otPlatDnssdAddressCallback AddressCallback; ///< Address callback + typedef otPlatDnssdBrowseResult BrowseResult; ///< Browser result. + typedef otPlatDnssdSrvResult SrvResult; ///< SRV result. + typedef otPlatDnssdTxtResult TxtResult; ///< TXT result. + typedef otPlatDnssdAddressResult AddressResult; ///< Address result. + typedef otPlatDnssdAddressAndTtl AddressAndTtl; ///< Address and TTL. class Host : public otPlatDnssdHost, public Clearable ///< Host information. { @@ -106,6 +115,22 @@ class Dnssd : public InstanceLocator, private NonCopyable { }; + class Browser : public otPlatDnssdBrowser, public Clearable ///< Browser. + { + }; + + class SrvResolver : public otPlatDnssdSrvResolver, public Clearable ///< SRV resolver. + { + }; + + class TxtResolver : public otPlatDnssdTxtResolver, public Clearable ///< TXT resolver. + { + }; + + class AddressResolver : public otPlatDnssdAddressResolver, public Clearable ///< Address resolver. + { + }; + /** * Represents a range of `RequestId` values. * @@ -274,6 +299,116 @@ class Dnssd : public InstanceLocator, private NonCopyable */ void UnregisterKey(const Key &aKey, RequestId aRequestId, RegisterCallback aCallback); + /** + * Starts a service browser. + * + * Refer to the documentation for `otPlatDnssdStartBrowser()` for a more detailed description of the behavior + * of this method. + * + * @param[in] aBrowser The browser to be started. + * + */ + void StartBrowser(const Browser &aBrowser); + + /** + * Stops a service browser. + * + * Refer to the documentation for `otPlatDnssdStopBrowser()` for a more detailed description of the behavior + * of this method. + * + * @param[in] aBrowser The browser to stop. + * + */ + void StopBrowser(const Browser &aBrowser); + + /** + * Starts an SRV record resolver. + * + * Refer to the documentation for `otPlatDnssdStartSrvResolver()` for a more detailed description of the behavior + * of this method. + * + * @param[in] aResolver The resolver to be started. + * + */ + void StartSrvResolver(const SrvResolver &aResolver); + + /** + * Stops an SRV record resolver. + * + * Refer to the documentation for `otPlatDnssdStopSrvResolver()` for a more detailed description of the behavior + * of this method. + * + * @param[in] aResolver The resolver to stop. + * + */ + void StopSrvResolver(const SrvResolver &aResolver); + + /** + * Starts a TXT record resolver. + * + * Refer to the documentation for `otPlatDnssdStartTxtResolver()` for a more detailed description of the behavior + * of this method. + * + * @param[in] aResolver The resolver to be started. + * + */ + void StartTxtResolver(const TxtResolver &aResolver); + + /** + * Stops a TXT record resolver. + * + * Refer to the documentation for `otPlatDnssdStopTxtResolver()` for a more detailed description of the behavior + * of this method. + * + * @param[in] aResolver The resolver to stop. + * + */ + void StopTxtResolver(const TxtResolver &aResolver); + + /** + * Starts an IPv6 address resolver. + * + * Refer to the documentation for `otPlatDnssdStartIp6AddressResolver()` for a more detailed description of the + * behavior of this method. + * + * @param[in] aResolver The resolver to be started. + * + */ + void StartIp6AddressResolver(const AddressResolver &aResolver); + + /** + * Stops an IPv6 address resolver. + * + * Refer to the documentation for `otPlatDnssdStopIp6AddressResolver()` for a more detailed description of the + * behavior of this method. + * + * @param[in] aResolver The resolver to stop. + * + */ + void StopIp6AddressResolver(const AddressResolver &aResolver); + + /** + * Starts an IPv4 address resolver. + * + * Refer to the documentation for `otPlatDnssdStartIp4AddressResolver()` for a more detailed description of the + * behavior of this method. + * + * @param[in] aResolver The resolver to be started. + * + */ + void StartIp4AddressResolver(const AddressResolver &aResolver); + + /** + * Stops an IPv4 address resolver. + * + * Refer to the documentation for `otPlatDnssdStopIp4AddressResolver()` for a more detailed description of the + * behavior of this method. + * + * @param[in] aResolver The resolver to stop. + * + */ + void StopIp4AddressResolver(const AddressResolver &aResolver); + #if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE /** * Handles native mDNS state change. diff --git a/src/core/net/dnssd_server.cpp b/src/core/net/dnssd_server.cpp index af24fe95e..35c6cb793 100644 --- a/src/core/net/dnssd_server.cpp +++ b/src/core/net/dnssd_server.cpp @@ -63,7 +63,10 @@ const char *Server::kBlockedDomains[] = {"ipv4only.arpa."}; Server::Server(Instance &aInstance) : InstanceLocator(aInstance) - , mSocket(aInstance) + , mSocket(aInstance, *this) +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + , mDiscoveryProxy(aInstance) +#endif #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE , mEnableUpstreamQuery(false) #endif @@ -79,7 +82,7 @@ Error Server::Start(void) VerifyOrExit(!IsRunning()); - SuccessOrExit(error = mSocket.Open(&Server::HandleUdpReceive, this)); + SuccessOrExit(error = mSocket.Open()); SuccessOrExit(error = mSocket.Bind(kPort, kBindUnspecifiedNetif ? Ip6::kNetifUnspecified : Ip6::kNetifThread)); #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE @@ -88,6 +91,10 @@ Error Server::Start(void) LogInfo("Started"); +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + mDiscoveryProxy.UpdateState(); +#endif + exit: if (error != kErrorNone) { @@ -104,6 +111,10 @@ void Server::Stop(void) Finalize(query, Header::kResponseServerFailure); } +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + mDiscoveryProxy.Stop(); +#endif + #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE for (UpstreamQueryTransaction &txn : mUpstreamQueryTransactions) { @@ -124,11 +135,6 @@ void Server::Stop(void) #endif } -void Server::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo)); -} - void Server::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Request request; @@ -157,7 +163,8 @@ void Server::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessag void Server::ProcessQuery(Request &aRequest) { - ResponseCode rcode = Header::kResponseSuccess; + ResponseCode rcode = Header::kResponseSuccess; + bool shouldRespond = true; Response response(GetInstance()); #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE @@ -187,7 +194,7 @@ void Server::ProcessQuery(Request &aRequest) SuccessOrExit(rcode); #endif - SuccessOrExit(rcode = aRequest.ParseQuestions(mTestMode)); + SuccessOrExit(rcode = aRequest.ParseQuestions(mTestMode, shouldRespond)); SuccessOrExit(rcode = response.AddQuestionsFrom(aRequest)); #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) @@ -219,7 +226,10 @@ void Server::ProcessQuery(Request &aRequest) response.SetResponseCode(rcode); } - response.Send(*aRequest.mMessageInfo); + if (shouldRespond) + { + response.Send(*aRequest.mMessageInfo); + } } Server::Response::Response(Instance &aInstance) @@ -290,7 +300,7 @@ void Server::Response::Send(const Ip6::MessageInfo &aMessageInfo) return; } -Server::ResponseCode Server::Request::ParseQuestions(uint8_t aTestMode) +Server::ResponseCode Server::Request::ParseQuestions(uint8_t aTestMode, bool &aShouldRespond) { // Parse header and questions from a `Request` query message and // determine the `QueryType`. @@ -300,6 +310,8 @@ Server::ResponseCode Server::Request::ParseQuestions(uint8_t aTestMode) uint16_t questionCount = mHeader.GetQuestionCount(); Question question; + aShouldRespond = true; + VerifyOrExit(mHeader.GetQueryType() == Header::kQueryTypeStandard, rcode = Header::kResponseNotImplemented); VerifyOrExit(!mHeader.IsTruncationFlagSet()); @@ -329,7 +341,8 @@ Server::ResponseCode Server::Request::ParseQuestions(uint8_t aTestMode) if (questionCount > 1) { - VerifyOrExit(!(aTestMode & kTestModeSingleQuestionOnly)); + VerifyOrExit(!(aTestMode & kTestModeRejectMultiQuestionQuery)); + VerifyOrExit(!(aTestMode & kTestModeIgnoreMultiQuestionQuery), aShouldRespond = false); VerifyOrExit(questionCount == 2); @@ -586,17 +599,25 @@ Error Server::Response::AppendHostAddresses(const Ip6::Address *aAddrs, uint16_t for (uint16_t index = 0; index < aAddrsLength; index++) { - AaaaRecord aaaaRecord; + SuccessOrExit(error = AppendAaaaRecord(aAddrs[index], aTtl)); + } + +exit: + return error; +} - aaaaRecord.Init(); - aaaaRecord.SetTtl(aTtl); - aaaaRecord.SetAddress(aAddrs[index]); +Error Server::Response::AppendAaaaRecord(const Ip6::Address &aAddress, uint32_t aTtl) +{ + Error error; + AaaaRecord aaaaRecord; - SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mHostName, *mMessage)); - SuccessOrExit(error = mMessage->Append(aaaaRecord)); + aaaaRecord.Init(); + aaaaRecord.SetTtl(aTtl); + aaaaRecord.SetAddress(aAddress); - IncResourceRecordCount(); - } + SuccessOrExit(error = Name::AppendPointerLabel(mOffsets.mHostName, *mMessage)); + SuccessOrExit(error = mMessage->Append(aaaaRecord)); + IncResourceRecordCount(); exit: return error; @@ -915,9 +936,12 @@ void Server::ResolveByProxy(Response &aResponse, const Ip6::MessageInfo &aMessag { ProxyQuery *query; ProxyQueryInfo info; - Name::Buffer name; +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + VerifyOrExit(mQuerySubscribe.IsSet() || mDiscoveryProxy.IsRunning()); +#else VerifyOrExit(mQuerySubscribe.IsSet()); +#endif // We try to convert `aResponse.mMessage` to a `ProxyQuery` by // appending `ProxyQueryInfo` to it. @@ -927,6 +951,10 @@ void Server::ResolveByProxy(Response &aResponse, const Ip6::MessageInfo &aMessag info.mExpireTime = TimerMilli::GetNow() + kQueryTimeout; info.mOffsets = aResponse.mOffsets; +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + info.mAction = kNoAction; +#endif + if (aResponse.mMessage->Append(info) != kErrorNone) { aResponse.SetResponseCode(Header::kResponseServerFailure); @@ -943,8 +971,21 @@ void Server::ResolveByProxy(Response &aResponse, const Ip6::MessageInfo &aMessag mTimer.FireAtIfEarlier(info.mExpireTime); - ReadQueryName(*query, name); - mQuerySubscribe.Invoke(name); +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + if (mQuerySubscribe.IsSet()) +#endif + { + Name::Buffer name; + + ReadQueryName(*query, name); + mQuerySubscribe.Invoke(name); + } +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + else + { + mDiscoveryProxy.Resolve(*query, info); + } +#endif exit: return; @@ -964,6 +1005,91 @@ bool Server::QueryNameMatches(const Message &aQuery, const char *aName) return (Name::CompareName(aQuery, offset, aName) == kErrorNone); } +void Server::ReadQueryInstanceName(const ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, Name::Buffer &aName) +{ + uint16_t offset = aInfo.mOffsets.mInstanceName; + + IgnoreError(Name::ReadName(aQuery, offset, aName, sizeof(aName))); +} + +void Server::ReadQueryInstanceName(const ProxyQuery &aQuery, + const ProxyQueryInfo &aInfo, + Name::LabelBuffer &aInstanceLabel, + Name::Buffer &aServiceType) +{ + // Reads the service instance label and service type with domain + // name stripped. + + uint16_t offset = aInfo.mOffsets.mInstanceName; + uint8_t labelLength = sizeof(aInstanceLabel); + + IgnoreError(Dns::Name::ReadLabel(aQuery, offset, aInstanceLabel, labelLength)); + IgnoreError(Dns::Name::ReadName(aQuery, offset, aServiceType)); + IgnoreError(StripDomainName(aServiceType)); +} + +bool Server::QueryInstanceNameMatches(const ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, const char *aName) +{ + uint16_t offset = aInfo.mOffsets.mInstanceName; + + return (Name::CompareName(aQuery, offset, aName) == kErrorNone); +} + +void Server::ReadQueryHostName(const ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, Name::Buffer &aName) +{ + uint16_t offset = aInfo.mOffsets.mHostName; + + IgnoreError(Name::ReadName(aQuery, offset, aName, sizeof(aName))); +} + +bool Server::QueryHostNameMatches(const ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, const char *aName) +{ + uint16_t offset = aInfo.mOffsets.mHostName; + + return (Name::CompareName(aQuery, offset, aName) == kErrorNone); +} + +Error Server::StripDomainName(Name::Buffer &aName) +{ + // In-place removes the domain name from `aName`. + + return Name::StripName(aName, kDefaultDomainName); +} + +Error Server::StripDomainName(const char *aFullName, Name::Buffer &aLabels) +{ + // Remove the domain name from `aFullName` and copies + // the result into `aLabels`. + + return Name::ExtractLabels(aFullName, kDefaultDomainName, aLabels, sizeof(aLabels)); +} + +void Server::ConstructFullName(const char *aLabels, Name::Buffer &aFullName) +{ + // Construct a full name by appending the default domain name + // to `aLabels`. + + StringWriter fullName(aFullName, sizeof(aFullName)); + + fullName.Append("%s.%s", aLabels, kDefaultDomainName); +} + +void Server::ConstructFullInstanceName(const char *aInstanceLabel, const char *aServiceType, Name::Buffer &aFullName) +{ + StringWriter fullName(aFullName, sizeof(aFullName)); + + fullName.Append("%s.%s.%s", aInstanceLabel, aServiceType, kDefaultDomainName); +} + +void Server::ConstructFullServiceSubTypeName(const char *aServiceType, + const char *aSubTypeLabel, + Name::Buffer &aFullName) +{ + StringWriter fullName(aFullName, sizeof(aFullName)); + + fullName.Append("%s._sub.%s.%s", aSubTypeLabel, aServiceType, kDefaultDomainName); +} + void Server::ProxyQueryInfo::ReadFrom(const ProxyQuery &aQuery) { SuccessOrAssert(aQuery.Read(aQuery.GetLength() - sizeof(ProxyQueryInfo), *this)); @@ -987,15 +1113,22 @@ Error Server::Response::ExtractServiceInstanceLabel(const char *aInstanceName, N return Name::ExtractLabels(aInstanceName, serviceName, aLabel); } -void Server::RemoveQueryAndPrepareResponse(ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, Response &aResponse) +void Server::RemoveQueryAndPrepareResponse(ProxyQuery &aQuery, ProxyQueryInfo &aInfo, Response &aResponse) { - Name::Buffer name; +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + mDiscoveryProxy.CancelAction(aQuery, aInfo); +#endif mProxyQueries.Dequeue(aQuery); aInfo.RemoveFrom(aQuery); - ReadQueryName(aQuery, name); - mQueryUnsubscribe.InvokeIfSet(name); + if (mQueryUnsubscribe.IsSet()) + { + Name::Buffer name; + + ReadQueryName(aQuery, name); + mQueryUnsubscribe.Invoke(name); + } aResponse.InitFrom(aQuery, aInfo); } @@ -1282,6 +1415,707 @@ void Server::ResetUpstreamQueryTransaction(UpstreamQueryTransaction &aTxn, Error } #endif +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + +Server::DiscoveryProxy::DiscoveryProxy(Instance &aInstance) + : InstanceLocator(aInstance) + , mIsRunning(false) +{ +} + +void Server::DiscoveryProxy::UpdateState(void) +{ + if (Get().IsRunning() && Get().IsReady() && Get().IsRunning()) + { + Start(); + } + else + { + Stop(); + } +} + +void Server::DiscoveryProxy::Start(void) +{ + VerifyOrExit(!mIsRunning); + mIsRunning = true; + LogInfo("Started discovery proxy"); + +exit: + return; +} + +void Server::DiscoveryProxy::Stop(void) +{ + VerifyOrExit(mIsRunning); + + for (ProxyQuery &query : Get().mProxyQueries) + { + Get().Finalize(query, Header::kResponseSuccess); + } + + mIsRunning = false; + LogInfo("Stopped discovery proxy"); + +exit: + return; +} + +void Server::DiscoveryProxy::Resolve(ProxyQuery &aQuery, ProxyQueryInfo &aInfo) +{ + ProxyAction action = kNoAction; + + switch (aInfo.mType) + { + case kPtrQuery: + action = kBrowsing; + break; + + case kSrvQuery: + case kSrvTxtQuery: + action = kResolvingSrv; + break; + + case kTxtQuery: + action = kResolvingTxt; + break; + + case kAaaaQuery: + action = kResolvingIp6Address; + break; + } + + Perform(action, aQuery, aInfo); +} + +void Server::DiscoveryProxy::Perform(ProxyAction aAction, ProxyQuery &aQuery, ProxyQueryInfo &aInfo) +{ + bool shouldStart; + Name::Buffer name; + + VerifyOrExit(aAction != kNoAction); + + // The order of the steps below is crucial. First, we read the + // name associated with the action. Then we check if another + // query has an active browser/resolver for the same name. This + // helps us determine if a new browser/resolver is needed. Then, + // we update the `ProxyQueryInfo` within `aQuery` to reflect the + // `aAction` being performed. Finally, if necessary, we start the + // proper browser/resolver on DNS-SD/mDNS. Placing this last + // ensures correct processing even if a DNS-SD/mDNS callback is + // invoked immediately. + + ReadNameFor(aAction, aQuery, aInfo, name); + + shouldStart = !HasActive(aAction, name); + + aInfo.mAction = aAction; + aInfo.UpdateIn(aQuery); + + VerifyOrExit(shouldStart); + UpdateProxy(kStart, aAction, aQuery, aInfo, name); + +exit: + return; +} + +void Server::DiscoveryProxy::ReadNameFor(ProxyAction aAction, + ProxyQuery &aQuery, + ProxyQueryInfo &aInfo, + Name::Buffer &aName) const +{ + // Read the name corresponding to `aAction` from `aQuery`. + + switch (aAction) + { + case kNoAction: + break; + case kBrowsing: + ReadQueryName(aQuery, aName); + break; + case kResolvingSrv: + case kResolvingTxt: + ReadQueryInstanceName(aQuery, aInfo, aName); + break; + case kResolvingIp6Address: + ReadQueryHostName(aQuery, aInfo, aName); + break; + } +} + +void Server::DiscoveryProxy::CancelAction(ProxyQuery &aQuery, ProxyQueryInfo &aInfo) +{ + // Cancel the current action for a given `aQuery`, then + // determine if we need to stop any browser/resolver + // on infrastructure. + + ProxyAction action = aInfo.mAction; + Name::Buffer name; + + VerifyOrExit(mIsRunning); + VerifyOrExit(action != kNoAction); + + // We first update the `aInfo` on `aQuery` before calling + // `HasActive()`. This ensures that the current query is not + // taken into account when we try to determine if any query + // is waiting for same `aAction` browser/resolver. + + ReadNameFor(action, aQuery, aInfo, name); + + aInfo.mAction = kNoAction; + aInfo.UpdateIn(aQuery); + + VerifyOrExit(!HasActive(action, name)); + UpdateProxy(kStop, action, aQuery, aInfo, name); + +exit: + return; +} + +void Server::DiscoveryProxy::UpdateProxy(Command aCommand, + ProxyAction aAction, + const ProxyQuery &aQuery, + const ProxyQueryInfo &aInfo, + Name::Buffer &aName) +{ + // Start or stop browser/resolver corresponding to `aAction`. + // `aName` may be changed. + + switch (aAction) + { + case kNoAction: + break; + case kBrowsing: + StartOrStopBrowser(aCommand, aName); + break; + case kResolvingSrv: + StartOrStopSrvResolver(aCommand, aQuery, aInfo); + break; + case kResolvingTxt: + StartOrStopTxtResolver(aCommand, aQuery, aInfo); + break; + case kResolvingIp6Address: + StartOrStopIp6Resolver(aCommand, aName); + break; + } +} + +void Server::DiscoveryProxy::StartOrStopBrowser(Command aCommand, Name::Buffer &aServiceName) +{ + // Start or stop a service browser for a given service type + // or sub-type. + + static const char kFullSubLabel[] = "._sub."; + + Dnssd::Browser browser; + char *ptr; + + browser.Clear(); + + IgnoreError(StripDomainName(aServiceName)); + + // Check if the service name is a sub-type with name + // format: "._sub.. + + ptr = AsNonConst(StringFind(aServiceName, kFullSubLabel, kStringCaseInsensitiveMatch)); + + if (ptr != nullptr) + { + *ptr = kNullChar; + ptr += sizeof(kFullSubLabel) - 1; + + browser.mServiceType = ptr; + browser.mSubTypeLabel = aServiceName; + } + else + { + browser.mServiceType = aServiceName; + browser.mSubTypeLabel = nullptr; + } + + browser.mInfraIfIndex = Get().GetIfIndex(); + browser.mCallback = HandleBrowseResult; + + switch (aCommand) + { + case kStart: + Get().StartBrowser(browser); + break; + + case kStop: + Get().StopBrowser(browser); + break; + } +} + +void Server::DiscoveryProxy::StartOrStopSrvResolver(Command aCommand, + const ProxyQuery &aQuery, + const ProxyQueryInfo &aInfo) +{ + // Start or stop an SRV record resolver for a given query. + + Dnssd::SrvResolver resolver; + Name::LabelBuffer instanceLabel; + Name::Buffer serviceType; + + ReadQueryInstanceName(aQuery, aInfo, instanceLabel, serviceType); + + resolver.Clear(); + + resolver.mServiceInstance = instanceLabel; + resolver.mServiceType = serviceType; + resolver.mInfraIfIndex = Get().GetIfIndex(); + resolver.mCallback = HandleSrvResult; + + switch (aCommand) + { + case kStart: + Get().StartSrvResolver(resolver); + break; + + case kStop: + Get().StopSrvResolver(resolver); + break; + } +} + +void Server::DiscoveryProxy::StartOrStopTxtResolver(Command aCommand, + const ProxyQuery &aQuery, + const ProxyQueryInfo &aInfo) +{ + // Start or stop a TXT record resolver for a given query. + + Dnssd::TxtResolver resolver; + Name::LabelBuffer instanceLabel; + Name::Buffer serviceType; + + ReadQueryInstanceName(aQuery, aInfo, instanceLabel, serviceType); + + resolver.Clear(); + + resolver.mServiceInstance = instanceLabel; + resolver.mServiceType = serviceType; + resolver.mInfraIfIndex = Get().GetIfIndex(); + resolver.mCallback = HandleTxtResult; + + switch (aCommand) + { + case kStart: + Get().StartTxtResolver(resolver); + break; + + case kStop: + Get().StopTxtResolver(resolver); + break; + } +} + +void Server::DiscoveryProxy::StartOrStopIp6Resolver(Command aCommand, Name::Buffer &aHostName) +{ + // Start or stop an IPv6 address resolver for a given host name. + + Dnssd::AddressResolver resolver; + + IgnoreError(StripDomainName(aHostName)); + + resolver.mHostName = aHostName; + resolver.mInfraIfIndex = Get().GetIfIndex(); + resolver.mCallback = HandleIp6AddressResult; + + switch (aCommand) + { + case kStart: + Get().StartIp6AddressResolver(resolver); + break; + + case kStop: + Get().StopIp6AddressResolver(resolver); + break; + } +} + +bool Server::DiscoveryProxy::QueryMatches(const ProxyQuery &aQuery, + const ProxyQueryInfo &aInfo, + ProxyAction aAction, + const Name::Buffer &aName) const +{ + // Check whether `aQuery` is performing `aAction` and + // its name matches `aName`. + + bool matches = false; + + VerifyOrExit(aInfo.mAction == aAction); + + switch (aAction) + { + case kBrowsing: + VerifyOrExit(QueryNameMatches(aQuery, aName)); + break; + case kResolvingSrv: + case kResolvingTxt: + VerifyOrExit(QueryInstanceNameMatches(aQuery, aInfo, aName)); + break; + case kResolvingIp6Address: + VerifyOrExit(QueryHostNameMatches(aQuery, aInfo, aName)); + break; + case kNoAction: + ExitNow(); + } + + matches = true; + +exit: + return matches; +} + +bool Server::DiscoveryProxy::HasActive(ProxyAction aAction, const Name::Buffer &aName) const +{ + // Determine whether or not we have an active browser/resolver + // corresponding to `aAction` for `aName`. + + bool has = false; + + for (const ProxyQuery &query : Get().mProxyQueries) + { + ProxyQueryInfo info; + + info.ReadFrom(query); + + if (QueryMatches(query, info, aAction, aName)) + { + has = true; + break; + } + } + + return has; +} + +void Server::DiscoveryProxy::HandleBrowseResult(otInstance *aInstance, const otPlatDnssdBrowseResult *aResult) +{ + AsCoreType(aInstance).Get().mDiscoveryProxy.HandleBrowseResult(*aResult); +} + +void Server::DiscoveryProxy::HandleBrowseResult(const Dnssd::BrowseResult &aResult) +{ + Name::Buffer serviceName; + + VerifyOrExit(mIsRunning); + VerifyOrExit(aResult.mTtl != 0); + VerifyOrExit(aResult.mInfraIfIndex == Get().GetIfIndex()); + + if (aResult.mSubTypeLabel != nullptr) + { + ConstructFullServiceSubTypeName(aResult.mServiceType, aResult.mSubTypeLabel, serviceName); + } + else + { + ConstructFullName(aResult.mServiceType, serviceName); + } + + HandleResult(kBrowsing, serviceName, &Response::AppendPtrRecord, ProxyResult(aResult)); + +exit: + return; +} + +void Server::DiscoveryProxy::HandleSrvResult(otInstance *aInstance, const otPlatDnssdSrvResult *aResult) +{ + AsCoreType(aInstance).Get().mDiscoveryProxy.HandleSrvResult(*aResult); +} + +void Server::DiscoveryProxy::HandleSrvResult(const Dnssd::SrvResult &aResult) +{ + Name::Buffer instanceName; + + VerifyOrExit(mIsRunning); + VerifyOrExit(aResult.mTtl != 0); + VerifyOrExit(aResult.mInfraIfIndex == Get().GetIfIndex()); + + ConstructFullInstanceName(aResult.mServiceInstance, aResult.mServiceType, instanceName); + HandleResult(kResolvingSrv, instanceName, &Response::AppendSrvRecord, ProxyResult(aResult)); + +exit: + return; +} + +void Server::DiscoveryProxy::HandleTxtResult(otInstance *aInstance, const otPlatDnssdTxtResult *aResult) +{ + AsCoreType(aInstance).Get().mDiscoveryProxy.HandleTxtResult(*aResult); +} + +void Server::DiscoveryProxy::HandleTxtResult(const Dnssd::TxtResult &aResult) +{ + Name::Buffer instanceName; + + VerifyOrExit(mIsRunning); + VerifyOrExit(aResult.mTtl != 0); + VerifyOrExit(aResult.mInfraIfIndex == Get().GetIfIndex()); + + ConstructFullInstanceName(aResult.mServiceInstance, aResult.mServiceType, instanceName); + HandleResult(kResolvingTxt, instanceName, &Response::AppendTxtRecord, ProxyResult(aResult)); + +exit: + return; +} + +void Server::DiscoveryProxy::HandleIp6AddressResult(otInstance *aInstance, const otPlatDnssdAddressResult *aResult) +{ + AsCoreType(aInstance).Get().mDiscoveryProxy.HandleIp6AddressResult(*aResult); +} + +void Server::DiscoveryProxy::HandleIp6AddressResult(const Dnssd::AddressResult &aResult) +{ + bool hasValidAddress = false; + Name::Buffer fullHostName; + + VerifyOrExit(mIsRunning); + VerifyOrExit(aResult.mInfraIfIndex == Get().GetIfIndex()); + + for (uint16_t index = 0; index < aResult.mAddressesLength; index++) + { + const Dnssd::AddressAndTtl &entry = aResult.mAddresses[index]; + const Ip6::Address &address = AsCoreType(&entry.mAddress); + + if (entry.mTtl == 0) + { + continue; + } + + if (IsProxyAddressValid(address)) + { + hasValidAddress = true; + break; + } + } + + VerifyOrExit(hasValidAddress); + + ConstructFullName(aResult.mHostName, fullHostName); + HandleResult(kResolvingIp6Address, fullHostName, &Response::AppendHostAddresses, ProxyResult(aResult)); + +exit: + return; +} + +void Server::DiscoveryProxy::HandleResult(ProxyAction aAction, + const Name::Buffer &aName, + ResponseAppender aAppender, + const ProxyResult &aResult) +{ + // Common method that handles result from DNS-SD/mDNS. It + // iterates over all `ProxyQuery` entries and checks if any entry + // is waiting for the result of `aAction` for `aName`. Matching + // queries are updated using the `aAppender` method pointer, + // which appends the corresponding record(s) to the response. We + // then determine the next action to be performed for the + // `ProxyQuery` or if it can be finalized. + + ProxyQueryList nextActionQueries; + ProxyQueryInfo info; + ProxyAction nextAction; + + for (ProxyQuery &query : Get().mProxyQueries) + { + Response response(GetInstance()); + bool shouldFinalize; + + info.ReadFrom(query); + + if (!QueryMatches(query, info, aAction, aName)) + { + continue; + } + + CancelAction(query, info); + + nextAction = kNoAction; + + switch (aAction) + { + case kBrowsing: + nextAction = kResolvingSrv; + break; + case kResolvingSrv: + nextAction = (info.mType == kSrvQuery) ? kResolvingIp6Address : kResolvingTxt; + break; + case kResolvingTxt: + nextAction = (info.mType == kTxtQuery) ? kNoAction : kResolvingIp6Address; + break; + case kNoAction: + case kResolvingIp6Address: + break; + } + + shouldFinalize = (nextAction == kNoAction); + + if ((Get().mTestMode & kTestModeEmptyAdditionalSection) && + IsActionForAdditionalSection(nextAction, info.mType)) + { + shouldFinalize = true; + } + + Get().mProxyQueries.Dequeue(query); + info.RemoveFrom(query); + response.InitFrom(query, info); + + if ((response.*aAppender)(aResult) != kErrorNone) + { + response.SetResponseCode(Header::kResponseServerFailure); + shouldFinalize = true; + } + + if (shouldFinalize) + { + response.Send(info.mMessageInfo); + continue; + } + + // The `query` is not yet finished and we need to perform + // the `nextAction` for it. + + // Reinitialize `response` as a `ProxyQuey` by updating + // and appending `info` to it after the newly appended + // records from `aResult` and saving the `mHeader`. + + info.mOffsets = response.mOffsets; + info.mAction = nextAction; + response.mMessage->Write(0, response.mHeader); + + if (response.mMessage->Append(info) != kErrorNone) + { + response.SetResponseCode(Header::kResponseServerFailure); + response.Send(info.mMessageInfo); + continue; + } + + // Take back ownership of `response.mMessage` as we still + // treat it as a `ProxyQuery`. + + response.mMessage.Release(); + + // We place the `query` in a separate list and add it back to + // the main `mProxyQueries` list after we are done with the + // current iteration. This ensures that other entries in the + // `mProxyQueries` list are not updated or removed due to the + // DNS-SD platform callback being invoked immediately when we + // potentially start a browser or resolver to perform the + // `nextAction` for `query`. + + nextActionQueries.Enqueue(query); + } + + for (ProxyQuery &query : nextActionQueries) + { + nextActionQueries.Dequeue(query); + + info.ReadFrom(query); + + nextAction = info.mAction; + + info.mAction = kNoAction; + info.UpdateIn(query); + + Get().mProxyQueries.Enqueue(query); + Perform(nextAction, query, info); + } +} + +bool Server::DiscoveryProxy::IsActionForAdditionalSection(ProxyAction aAction, QueryType aQueryType) +{ + bool isForAddnlSection = false; + + switch (aAction) + { + case kResolvingSrv: + VerifyOrExit((aQueryType == kSrvQuery) || (aQueryType == kSrvTxtQuery)); + break; + case kResolvingTxt: + VerifyOrExit((aQueryType == kTxtQuery) || (aQueryType == kSrvTxtQuery)); + break; + + case kResolvingIp6Address: + VerifyOrExit(aQueryType == kAaaaQuery); + break; + + case kNoAction: + case kBrowsing: + ExitNow(); + } + + isForAddnlSection = true; + +exit: + return isForAddnlSection; +} + +Error Server::Response::AppendPtrRecord(const ProxyResult &aResult) +{ + const Dnssd::BrowseResult *browseResult = aResult.mBrowseResult; + + mSection = kAnswerSection; + + return AppendPtrRecord(browseResult->mServiceInstance, browseResult->mTtl); +} + +Error Server::Response::AppendSrvRecord(const ProxyResult &aResult) +{ + const Dnssd::SrvResult *srvResult = aResult.mSrvResult; + Name::Buffer fullHostName; + + mSection = ((mType == kSrvQuery) || (mType == kSrvTxtQuery)) ? kAnswerSection : kAdditionalDataSection; + + ConstructFullName(srvResult->mHostName, fullHostName); + + return AppendSrvRecord(fullHostName, srvResult->mTtl, srvResult->mPriority, srvResult->mWeight, srvResult->mPort); +} + +Error Server::Response::AppendTxtRecord(const ProxyResult &aResult) +{ + const Dnssd::TxtResult *txtResult = aResult.mTxtResult; + + mSection = ((mType == kTxtQuery) || (mType == kSrvTxtQuery)) ? kAnswerSection : kAdditionalDataSection; + + return AppendTxtRecord(txtResult->mTxtData, txtResult->mTxtDataLength, txtResult->mTtl); +} + +Error Server::Response::AppendHostAddresses(const ProxyResult &aResult) +{ + Error error = kErrorNone; + const Dnssd::AddressResult *addrResult = aResult.mAddressResult; + + mSection = (mType == kAaaaQuery) ? kAnswerSection : kAdditionalDataSection; + + for (uint16_t index = 0; index < addrResult->mAddressesLength; index++) + { + const Dnssd::AddressAndTtl &entry = addrResult->mAddresses[index]; + const Ip6::Address &address = AsCoreType(&entry.mAddress); + + if (entry.mTtl == 0) + { + continue; + } + + if (!IsProxyAddressValid(address)) + { + continue; + } + + SuccessOrExit(error = AppendAaaaRecord(address, entry.mTtl)); + } + +exit: + return error; +} + +bool Server::IsProxyAddressValid(const Ip6::Address &aAddress) +{ + return !aAddress.IsLinkLocalUnicast() && !aAddress.IsMulticast() && !aAddress.IsUnspecified() && + !aAddress.IsLoopback(); +} + +#endif // OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + } // namespace ServiceDiscovery } // namespace Dns } // namespace ot diff --git a/src/core/net/dnssd_server.hpp b/src/core/net/dnssd_server.hpp index 4afde1d7d..f584e3b12 100644 --- a/src/core/net/dnssd_server.hpp +++ b/src/core/net/dnssd_server.hpp @@ -33,8 +33,20 @@ #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + +#if !OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE && !OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE +#error "OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE requires either PLATFORM_DNSSD_ENABLE or MULTICAST_DNS_ENABLE" +#endif +#if !OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#error "OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE requires OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE" +#endif + +#endif // OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + #include +#include "border_router/infra_if.hpp" #include "common/as_core_type.hpp" #include "common/callback.hpp" #include "common/message.hpp" @@ -42,6 +54,7 @@ #include "common/owned_ptr.hpp" #include "common/timer.hpp" #include "net/dns_types.hpp" +#include "net/dnssd.hpp" #include "net/ip6.hpp" #include "net/netif.hpp" #include "net/srp_server.hpp" @@ -71,6 +84,10 @@ namespace ServiceDiscovery { class Server : public InstanceLocator, private NonCopyable { friend class Srp::Server; +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + friend class ot::Dnssd; + friend class ot::BorderRouter::InfraIf; +#endif public: /** @@ -274,8 +291,9 @@ class Server : public InstanceLocator, private NonCopyable */ enum TestModeFlags : uint8_t { - kTestModeSingleQuestionOnly = 1 << 0, ///< Allow single question in query, send `FormatError` otherwise. - kTestModeEmptyAdditionalSection = 1 << 1, ///< Do not include any RR in additional section. + kTestModeRejectMultiQuestionQuery = 1 << 0, ///< Send `FormatError` for a query with multiple questions. + kTestModeIgnoreMultiQuestionQuery = 1 << 1, ///< Ignore a query with multiple questions (send no response). + kTestModeEmptyAdditionalSection = 1 << 2, ///< Do not include any RR in additional section. }; static constexpr uint8_t kTestModeDisabled = 0; ///< Test mode is disabled (no flags). @@ -316,9 +334,20 @@ class Server : public InstanceLocator, private NonCopyable kAdditionalDataSection, }; +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + enum ProxyAction : uint8_t + { + kNoAction, + kBrowsing, + kResolvingSrv, + kResolvingTxt, + kResolvingIp6Address, + }; +#endif + struct Request { - ResponseCode ParseQuestions(uint8_t aTestMode); + ResponseCode ParseQuestions(uint8_t aTestMode, bool &aShouldRespond); const Message *mMessage; const Ip6::MessageInfo *mMessageInfo; @@ -336,6 +365,21 @@ class Server : public InstanceLocator, private NonCopyable uint16_t mHostName; }; +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + union ProxyResult + { + explicit ProxyResult(const Dnssd::BrowseResult &aBrowseResult) { mBrowseResult = &aBrowseResult; } + explicit ProxyResult(const Dnssd::SrvResult &aSrvResult) { mSrvResult = &aSrvResult; } + explicit ProxyResult(const Dnssd::TxtResult &aTxtResult) { mTxtResult = &aTxtResult; } + explicit ProxyResult(const Dnssd::AddressResult &aAddressResult) { mAddressResult = &aAddressResult; } + + const Dnssd::BrowseResult *mBrowseResult; + const Dnssd::SrvResult *mSrvResult; + const Dnssd::TxtResult *mTxtResult; + const Dnssd::AddressResult *mAddressResult; + }; +#endif + class Response : public InstanceLocator, private NonCopyable { public: @@ -360,6 +404,7 @@ class Server : public InstanceLocator, private NonCopyable Error AppendHostAddresses(const HostInfo &aHostInfo); Error AppendHostAddresses(const ServiceInstanceInfo &aInstanceInfo); Error AppendHostAddresses(const Ip6::Address *aAddrs, uint16_t aAddrsLength, uint32_t aTtl); + Error AppendAaaaRecord(const Ip6::Address &aAddress, uint32_t aTtl); void UpdateRecordLength(ResourceRecord &aRecord, uint16_t aOffset); void IncResourceRecordCount(void); void Send(const Ip6::MessageInfo &aMessageInfo); @@ -373,6 +418,14 @@ class Server : public InstanceLocator, private NonCopyable Error AppendTxtRecord(const Srp::Server::Service &aService); Error AppendHostAddresses(const Srp::Server::Host &aHost); #endif +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + Error AppendPtrRecord(const ProxyResult &aResult); + Error AppendSrvRecord(const ProxyResult &aResult); + Error AppendTxtRecord(const ProxyResult &aResult); + + Error AppendHostAddresses(const ProxyResult &aResult); +#endif + #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) void Log(void) const; static const char *QueryTypeToString(QueryType aType); @@ -395,18 +448,104 @@ class Server : public InstanceLocator, private NonCopyable Ip6::MessageInfo mMessageInfo; TimeMilli mExpireTime; NameOffsets mOffsets; +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + ProxyAction mAction; +#endif }; - bool IsRunning(void) const { return mSocket.IsBound(); } - static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - void ProcessQuery(Request &aRequest); +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + class DiscoveryProxy : public InstanceLocator, private NonCopyable + { + friend ot::Dnssd; + + public: + explicit DiscoveryProxy(Instance &aInstance); + + bool IsRunning(void) const { return mIsRunning; } + void UpdateState(void); + void Start(void); + void Stop(void); + void Resolve(ProxyQuery &aQuery, ProxyQueryInfo &aInfo); + void CancelAction(ProxyQuery &aQuery, ProxyQueryInfo &aInfo); - void ResolveByProxy(Response &aResponse, const Ip6::MessageInfo &aMessageInfo); - void RemoveQueryAndPrepareResponse(ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, Response &aResponse); - void Finalize(ProxyQuery &aQuery, ResponseCode aResponseCode); - static void ReadQueryName(const Message &aQuery, Name::Buffer &aName); - static bool QueryNameMatches(const Message &aQuery, const char *aName); + private: + enum Command : uint8_t + { + kStart, + kStop, + }; + + typedef Error (Response::*ResponseAppender)(const ProxyResult &aResult); + + void Perform(ProxyAction aAction, ProxyQuery &aQuery, ProxyQueryInfo &aInfo); + void ReadNameFor(ProxyAction aAction, ProxyQuery &aQuery, ProxyQueryInfo &aInfo, Name::Buffer &aName) const; + bool HasActive(ProxyAction aAction, const Name::Buffer &aName) const; + bool QueryMatches(const ProxyQuery &aQuery, + const ProxyQueryInfo &aInfo, + ProxyAction aAction, + const Name::Buffer &aName) const; + void UpdateProxy(Command aCommand, + ProxyAction aAction, + const ProxyQuery &aQuery, + const ProxyQueryInfo &aInfo, + Name::Buffer &aName); + void StartOrStopBrowser(Command aCommand, Name::Buffer &aServiceName); + void StartOrStopSrvResolver(Command aCommand, const ProxyQuery &aQuery, const ProxyQueryInfo &aInfo); + void StartOrStopTxtResolver(Command aCommand, const ProxyQuery &aQuery, const ProxyQueryInfo &aInfo); + void StartOrStopIp6Resolver(Command aCommand, Name::Buffer &aHostName); + + static void HandleBrowseResult(otInstance *aInstance, const otPlatDnssdBrowseResult *aResult); + static void HandleSrvResult(otInstance *aInstance, const otPlatDnssdSrvResult *aResult); + static void HandleTxtResult(otInstance *aInstance, const otPlatDnssdTxtResult *aResult); + static void HandleIp6AddressResult(otInstance *aInstance, const otPlatDnssdAddressResult *aResult); + + void HandleBrowseResult(const Dnssd::BrowseResult &aResult); + void HandleSrvResult(const Dnssd::SrvResult &aResult); + void HandleTxtResult(const Dnssd::TxtResult &aResult); + void HandleIp6AddressResult(const Dnssd::AddressResult &aResult); + void HandleResult(ProxyAction aAction, + const Name::Buffer &aName, + ResponseAppender aAppender, + const ProxyResult &aResult); + + static bool IsActionForAdditionalSection(ProxyAction aAction, QueryType aQueryType); + + bool mIsRunning; + }; +#endif + + bool IsRunning(void) const { return mSocket.IsBound(); } + void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + void ProcessQuery(Request &aRequest); + void ResolveByProxy(Response &aResponse, const Ip6::MessageInfo &aMessageInfo); + void RemoveQueryAndPrepareResponse(ProxyQuery &aQuery, ProxyQueryInfo &aInfo, Response &aResponse); + void Finalize(ProxyQuery &aQuery, ResponseCode aResponseCode); + + static void ReadQueryName(const Message &aQuery, Name::Buffer &aName); + static bool QueryNameMatches(const Message &aQuery, const char *aName); + static void ReadQueryInstanceName(const ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, Name::Buffer &aName); + static void ReadQueryInstanceName(const ProxyQuery &aQuery, + const ProxyQueryInfo &aInfo, + Name::LabelBuffer &aInstanceLabel, + Name::Buffer &aServiceType); + static bool QueryInstanceNameMatches(const ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, const char *aName); + static void ReadQueryHostName(const ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, Name::Buffer &aName); + static bool QueryHostNameMatches(const ProxyQuery &aQuery, const ProxyQueryInfo &aInfo, const char *aName); + static Error StripDomainName(const char *aFullName, Name::Buffer &aLabels); + static Error StripDomainName(Name::Buffer &aName); + static void ConstructFullName(const char *aLabels, Name::Buffer &aFullName); + static void ConstructFullInstanceName(const char *aInstanceLabel, + const char *aServiceType, + Name::Buffer &aFullName); + static void ConstructFullServiceSubTypeName(const char *aServiceType, + const char *aSubTypeLabel, + Name::Buffer &aFullName); + +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + void HandleInfraIfStateChanged(void) { mDiscoveryProxy.UpdateState(); } + void HandleDnssdPlatformStateChange(void) { mDiscoveryProxy.UpdateState(); } + static bool IsProxyAddressValid(const Ip6::Address &aAddress); +#endif #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE static bool ShouldForwardToUpstream(const Request &aRequest); @@ -420,7 +559,8 @@ class Server : public InstanceLocator, private NonCopyable void UpdateResponseCounters(ResponseCode aResponseCode); - using ServerTimer = TimerMilliIn; + using ServerTimer = TimerMilliIn; + using ServerSocket = Ip6::Udp::SocketIn; static const char kDefaultDomainName[]; static const char kSubLabel[]; @@ -428,12 +568,16 @@ class Server : public InstanceLocator, private NonCopyable static const char *kBlockedDomains[]; #endif - Ip6::Udp::Socket mSocket; + ServerSocket mSocket; ProxyQueryList mProxyQueries; Callback mQuerySubscribe; Callback mQueryUnsubscribe; +#if OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE + DiscoveryProxy mDiscoveryProxy; +#endif + #if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE bool mEnableUpstreamQuery; UpstreamQueryTransaction mUpstreamQueryTransactions[kMaxConcurrentUpstreamQueries]; diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index 0740e11a3..a8ce030e4 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -654,8 +654,8 @@ Error Ip6::HandleFragment(Message &aMessage) offset = FragmentHeader::FragmentOffsetToBytes(fragmentHeader.GetOffset()); payloadFragment = aMessage.GetLength() - aMessage.GetOffset() - sizeof(fragmentHeader); - LogInfo("Fragment with id %d received > %d bytes, offset %d", fragmentHeader.GetIdentification(), payloadFragment, - offset); + LogInfo("Fragment with id %lu received > %u bytes, offset %u", ToUlong(fragmentHeader.GetIdentification()), + payloadFragment, offset); if (offset + payloadFragment + aMessage.GetOffset() > kMaxAssembledDatagramLength) { @@ -1025,7 +1025,8 @@ Error Ip6::PassToHost(OwnedPtr &aMessagePtr, // than realm-local, set the hop limit to 1 before sending to host, so this packet // will not be forwarded by host. if (aMessageInfo.GetSockAddr().IsMulticastLargerThanRealmLocal() && - (aMessageInfo.GetPeerAddr().IsLinkLocal() || (Get().IsMeshLocalAddress(aMessageInfo.GetPeerAddr())))) + (aMessageInfo.GetPeerAddr().IsLinkLocalUnicast() || + (Get().IsMeshLocalAddress(aMessageInfo.GetPeerAddr())))) { messagePtr->Write(Header::kHopLimitFieldOffset, 1); } @@ -1122,10 +1123,6 @@ Error Ip6::HandleDatagram(OwnedPtr aMessagePtr, bool aIsReassembled) { receive = true; } - else if (Get().IsMulticastPromiscuousEnabled()) - { - forwardHost = true; - } } else { @@ -1135,9 +1132,9 @@ Error Ip6::HandleDatagram(OwnedPtr aMessagePtr, bool aIsReassembled) { receive = true; } - else if (!aMessagePtr->IsOriginThreadNetif() || !header.GetDestination().IsLinkLocal()) + else if (!aMessagePtr->IsOriginThreadNetif() || !header.GetDestination().IsLinkLocalUnicast()) { - if (header.GetDestination().IsLinkLocal()) + if (header.GetDestination().IsLinkLocalUnicast()) { forwardThread = true; } @@ -1434,8 +1431,8 @@ void Ip6::UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLe otPacketsAndBytes *counter = nullptr; otPacketsAndBytes *internetCounter = nullptr; - VerifyOrExit(!aHeader.GetSource().IsLinkLocal()); - VerifyOrExit(!aHeader.GetDestination().IsLinkLocal()); + VerifyOrExit(!aHeader.GetSource().IsLinkLocalUnicast()); + VerifyOrExit(!aHeader.GetDestination().IsLinkLocalUnicast()); VerifyOrExit(aHeader.GetSource().GetPrefix() != Get().GetMeshLocalPrefix()); VerifyOrExit(aHeader.GetDestination().GetPrefix() != Get().GetMeshLocalPrefix()); diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp index d82dfb4f1..acd443dae 100644 --- a/src/core/net/ip6_address.cpp +++ b/src/core/net/ip6_address.cpp @@ -343,7 +343,7 @@ bool Address::IsLoopback(void) const mFields.m32[3] == BigEndian::HostSwap32(1)); } -bool Address::IsLinkLocal(void) const +bool Address::IsLinkLocalUnicast(void) const { return (mFields.m16[0] & BigEndian::HostSwap16(0xffc0)) == BigEndian::HostSwap16(0xfe80); } @@ -364,6 +364,8 @@ void Address::SetToLinkLocalAddress(const InterfaceIdentifier &aIid) bool Address::IsLinkLocalMulticast(void) const { return IsMulticast() && (GetScope() == kLinkLocalScope); } +bool Address::IsLinkLocalUnicastOrMulticast(void) const { return IsLinkLocalUnicast() || IsLinkLocalMulticast(); } + bool Address::IsLinkLocalAllNodesMulticast(void) const { return (*this == GetLinkLocalAllNodesMulticast()); } void Address::SetToLinkLocalAllNodesMulticast(void) { *this = GetLinkLocalAllNodesMulticast(); } @@ -458,7 +460,7 @@ uint8_t Address::GetScope(void) const { rval = mFields.m8[1] & 0xf; } - else if (IsLinkLocal()) + else if (IsLinkLocalUnicast()) { rval = kLinkLocalScope; } diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp index d7dfe4969..5f06cd66f 100644 --- a/src/core/net/ip6_address.hpp +++ b/src/core/net/ip6_address.hpp @@ -646,13 +646,13 @@ class Address : public otIp6Address, public Equatable
, public Clearable bool IsLoopback(void) const; /** - * Indicates whether or not the IPv6 address scope is Link-Local. + * Indicates whether or not the IPv6 address is a Link-Local unicast address. * - * @retval TRUE If the IPv6 address scope is Link-Local. - * @retval FALSE If the IPv6 address scope is not Link-Local. + * @retval TRUE If the IPv6 address is a Link-Local unicast address. + * @retval FALSE If the IPv6 address is not a Link-Local unicast address. * */ - bool IsLinkLocal(void) const; + bool IsLinkLocalUnicast(void) const; /** * Sets the IPv6 address to a Link-Local address with Interface Identifier generated from a given @@ -689,6 +689,15 @@ class Address : public otIp6Address, public Equatable
, public Clearable */ bool IsLinkLocalMulticast(void) const; + /** + * Indicates whether or not the IPv6 address is a link-local unicast or a link-local multicast address. + * + * @retval TRUE If the IPv6 address is a link-local unicast or multicast address. + * @retval FALSE If the IPv6 address is not a link-local unicast and not a link-local multicast address. + * + */ + bool IsLinkLocalUnicastOrMulticast(void) const; + /** * Indicates whether or not the IPv6 address is a link-local all nodes multicast address (ff02::01). * diff --git a/src/core/net/ip6_filter.cpp b/src/core/net/ip6_filter.cpp index 1dae10c71..81e9c2178 100644 --- a/src/core/net/ip6_filter.cpp +++ b/src/core/net/ip6_filter.cpp @@ -64,9 +64,7 @@ bool Filter::Accept(Message &aMessage) const SuccessOrExit(headers.ParseFrom(aMessage)); - // Allow only link-local unicast or multicast - VerifyOrExit(headers.GetDestinationAddress().IsLinkLocal() || - headers.GetDestinationAddress().IsLinkLocalMulticast()); + VerifyOrExit(headers.GetDestinationAddress().IsLinkLocalUnicastOrMulticast()); // Allow all link-local IPv6 datagrams when Thread is not enabled if (Get().GetRole() == Mle::kRoleDisabled) diff --git a/src/core/net/ip6_mpl.cpp b/src/core/net/ip6_mpl.cpp index 7e18b39c6..4f3964d7b 100644 --- a/src/core/net/ip6_mpl.cpp +++ b/src/core/net/ip6_mpl.cpp @@ -76,7 +76,7 @@ void MplOption::Init(SeedIdLength aSeedIdLength) void Mpl::InitOption(MplOption &aOption, const Address &aAddress) { - if (aAddress == Get().GetMeshLocal16()) + if (aAddress == Get().GetMeshLocalRloc()) { // Seed ID can be elided when `aAddress` is RLOC. aOption.Init(MplOption::kSeedIdLength0); diff --git a/src/core/net/mdns.cpp b/src/core/net/mdns.cpp index 8fe50f77e..2ebad6519 100644 --- a/src/core/net/mdns.cpp +++ b/src/core/net/mdns.cpp @@ -391,12 +391,13 @@ bool Core::NameMatch(const Heap::String &aFirst, const Heap::String &aSecond) return !aSecond.IsNull() && NameMatch(aFirst, aSecond.AsCString()); } -void Core::UpdateCacheFlushFlagIn(ResourceRecord &aResourceRecord, Section aSection) +void Core::UpdateCacheFlushFlagIn(ResourceRecord &aResourceRecord, Section aSection, bool aIsLegacyUnicast) { - // Do not set the cache-flush flag is the record is - // appended in Authority Section in a probe message. + // Do not set the cache-flush flag if the record is + // appended in Authority Section in a probe message, + // or is intended for a Legacy Unicast response. - if (aSection != kAuthoritySection) + if (aSection != kAuthoritySection && !aIsLegacyUnicast) { aResourceRecord.SetClass(aResourceRecord.GetClass() | kClassCacheFlushFlag); } @@ -579,6 +580,11 @@ void Core::RecordInfo::UpdateProperty(AddressArray &aAddrProperty, const Ip6::Ad } } +uint32_t Core::RecordInfo::GetTtl(bool aIsLegacyUnicast) const +{ + return aIsLegacyUnicast ? Min(kMaxLegacyUnicastTtl, mTtl) : mTtl; +} + void Core::RecordInfo::UpdateTtl(uint32_t aTtl) { return UpdateProperty(mTtl, aTtl); } void Core::RecordInfo::StartAnnouncing(void) @@ -596,7 +602,7 @@ void Core::RecordInfo::ScheduleAnswer(const AnswerInfo &aInfo) { VerifyOrExit(CanAnswer()); - if (aInfo.mUnicastResponse) + if (aInfo.mUnicastResponse || aInfo.mLegacyUnicastResponse) { mUnicastAnswerPending = true; ExitNow(); @@ -647,6 +653,7 @@ bool Core::RecordInfo::ShouldAppendTo(TxMessage &aResponse, TimeMilli aNow) cons break; case TxMessage::kUnicastResponse: + case TxMessage::kLegacyUnicastResponse: shouldAppend = mUnicastAnswerPending; break; @@ -693,6 +700,7 @@ void Core::RecordInfo::UpdateStateAfterAnswer(const TxMessage &aResponse) break; case TxMessage::kUnicastResponse: + case TxMessage::kLegacyUnicastResponse: VerifyOrExit(IsAppended()); VerifyOrExit(mAppendSection == kAnswerSection); mUnicastAnswerPending = false; @@ -765,6 +773,7 @@ void Core::RecordInfo::MarkAsAppended(TxMessage &aTxMessage, Section aSection) break; case TxMessage::kUnicastResponse: + case TxMessage::kLegacyUnicastResponse: mAppendState = kAppendedInUnicastMsg; break; @@ -1229,7 +1238,6 @@ template void Core::Entry::HandleTimer(EntryTimerContext &a case kRemoving: ExitNow(); } - thisAsEntryType->DetermineNextFireTime(); exit: @@ -1258,6 +1266,7 @@ void Core::Entry::AppendKeyRecordTo(TxMessage &aTxMessage, Section aSection, Nam { Message *message; ResourceRecord record; + bool isLegacyUnicast = (aTxMessage.GetType() == TxMessage::kLegacyUnicastResponse); VerifyOrExit(mKeyRecord.CanAppend()); mKeyRecord.MarkAsAppended(aTxMessage, aSection); @@ -1270,9 +1279,9 @@ void Core::Entry::AppendKeyRecordTo(TxMessage &aTxMessage, Section aSection, Nam aNameAppender(*this, aTxMessage, aSection); record.Init(ResourceRecord::kTypeKey); - record.SetTtl(mKeyRecord.GetTtl()); record.SetLength(mKeyData.GetLength()); - UpdateCacheFlushFlagIn(record, aSection); + record.SetTtl(mKeyRecord.GetTtl(isLegacyUnicast)); + UpdateCacheFlushFlagIn(record, aSection, isLegacyUnicast); SuccessOrAssert(message->Append(record)); SuccessOrAssert(message->AppendBytes(mKeyData.GetBytes(), mKeyData.GetLength())); @@ -1292,10 +1301,11 @@ void Core::Entry::AppendNsecRecordTo(TxMessage &aTxMessage, NsecRecord nsec; NsecRecord::TypeBitMap bitmap; uint16_t offset; + bool isLegacyUnicast = (aTxMessage.GetType() == TxMessage::kLegacyUnicastResponse); nsec.Init(); - nsec.SetTtl(kNsecTtl); - UpdateCacheFlushFlagIn(nsec, aSection); + nsec.SetTtl(isLegacyUnicast ? kLegacyUnicastNsecTtl : kNsecTtl); + UpdateCacheFlushFlagIn(nsec, aSection, isLegacyUnicast); bitmap.Clear(); @@ -1606,6 +1616,7 @@ void Core::HostEntry::DetermineNextFireTime(void) void Core::HostEntry::AppendAddressRecordsTo(TxMessage &aTxMessage, Section aSection) { Message *message; + bool isLegacyUnicast = (aTxMessage.GetType() == TxMessage::kLegacyUnicastResponse); VerifyOrExit(mAddrRecord.CanAppend()); mAddrRecord.MarkAsAppended(aTxMessage, aSection); @@ -1617,9 +1628,9 @@ void Core::HostEntry::AppendAddressRecordsTo(TxMessage &aTxMessage, Section aSec AaaaRecord aaaaRecord; aaaaRecord.Init(); - aaaaRecord.SetTtl(mAddrRecord.GetTtl()); aaaaRecord.SetAddress(address); - UpdateCacheFlushFlagIn(aaaaRecord, aSection); + aaaaRecord.SetTtl(mAddrRecord.GetTtl(isLegacyUnicast)); + UpdateCacheFlushFlagIn(aaaaRecord, aSection, isLegacyUnicast); AppendNameTo(aTxMessage, aSection); SuccessOrAssert(message->Append(aaaaRecord)); @@ -2383,6 +2394,7 @@ void Core::ServiceEntry::AppendSrvRecordTo(TxMessage &aTxMessage, Section aSecti Message *message; SrvRecord srv; uint16_t offset; + bool isLegacyUnicast = (aTxMessage.GetType() == TxMessage::kLegacyUnicastResponse); VerifyOrExit(mSrvRecord.CanAppend()); mSrvRecord.MarkAsAppended(aTxMessage, aSection); @@ -2390,13 +2402,17 @@ void Core::ServiceEntry::AppendSrvRecordTo(TxMessage &aTxMessage, Section aSecti message = &aTxMessage.SelectMessageFor(aSection); srv.Init(); - srv.SetTtl(mSrvRecord.GetTtl()); srv.SetPriority(mPriority); srv.SetWeight(mWeight); srv.SetPort(mPort); - UpdateCacheFlushFlagIn(srv, aSection); + srv.SetTtl(mSrvRecord.GetTtl(isLegacyUnicast)); + UpdateCacheFlushFlagIn(srv, aSection, isLegacyUnicast); + + // RFC6762, Section 18.14 Name Compression: + // In legacy unicast responses generated to answer legacy queries, name + // compression MUST NOT be performed on SRV records. + AppendServiceNameTo(aTxMessage, aSection, /* aPerformNameCompression */ !isLegacyUnicast); - AppendServiceNameTo(aTxMessage, aSection); offset = message->GetLength(); SuccessOrAssert(message->Append(srv)); AppendHostNameTo(aTxMessage, aSection); @@ -2412,6 +2428,7 @@ void Core::ServiceEntry::AppendTxtRecordTo(TxMessage &aTxMessage, Section aSecti { Message *message; TxtRecord txt; + bool isLegacyUnicast = (aTxMessage.GetType() == TxMessage::kLegacyUnicastResponse); VerifyOrExit(mTxtRecord.CanAppend()); mTxtRecord.MarkAsAppended(aTxMessage, aSection); @@ -2419,9 +2436,9 @@ void Core::ServiceEntry::AppendTxtRecordTo(TxMessage &aTxMessage, Section aSecti message = &aTxMessage.SelectMessageFor(aSection); txt.Init(); - txt.SetTtl(mTxtRecord.GetTtl()); txt.SetLength(mTxtData.GetLength()); - UpdateCacheFlushFlagIn(txt, aSection); + txt.SetTtl(mTxtRecord.GetTtl(isLegacyUnicast)); + UpdateCacheFlushFlagIn(txt, aSection, isLegacyUnicast); AppendServiceNameTo(aTxMessage, aSection); SuccessOrAssert(message->Append(txt)); @@ -2442,6 +2459,7 @@ void Core::ServiceEntry::AppendPtrRecordTo(TxMessage &aTxMessage, Section aSecti RecordInfo &ptrRecord = (aSubType == nullptr) ? mPtrRecord : aSubType->mPtrRecord; PtrRecord ptr; uint16_t offset; + bool isLegacyUnicast = (aTxMessage.GetType() == TxMessage::kLegacyUnicastResponse); VerifyOrExit(ptrRecord.CanAppend()); ptrRecord.MarkAsAppended(aTxMessage, aSection); @@ -2449,7 +2467,7 @@ void Core::ServiceEntry::AppendPtrRecordTo(TxMessage &aTxMessage, Section aSecti message = &aTxMessage.SelectMessageFor(aSection); ptr.Init(); - ptr.SetTtl(ptrRecord.GetTtl()); + ptr.SetTtl(ptrRecord.GetTtl(isLegacyUnicast)); if (aSubType == nullptr) { @@ -2506,12 +2524,22 @@ void Core::ServiceEntry::AppendEntryName(Entry &aEntry, TxMessage &aTxMessage, S static_cast(aEntry).AppendServiceNameTo(aTxMessage, aSection); } -void Core::ServiceEntry::AppendServiceNameTo(TxMessage &aTxMessage, Section aSection) +void Core::ServiceEntry::AppendServiceNameTo(TxMessage &aTxMessage, Section aSection, bool aPerformNameCompression) { AppendOutcome outcome; - outcome = aTxMessage.AppendLabel(aSection, mServiceInstance.AsCString(), mServiceNameOffset); - VerifyOrExit(outcome != kAppendedFullNameAsCompressed); + if (!aPerformNameCompression) + { + uint16_t compressOffset = kUnspecifiedOffset; + + outcome = aTxMessage.AppendLabel(aSection, mServiceInstance.AsCString(), compressOffset); + VerifyOrExit(outcome == kAppendedLabels); + } + else + { + outcome = aTxMessage.AppendLabel(aSection, mServiceInstance.AsCString(), mServiceNameOffset); + VerifyOrExit(outcome != kAppendedFullNameAsCompressed); + } AppendServiceTypeTo(aTxMessage, aSection); @@ -2775,7 +2803,14 @@ void Core::ServiceType::AppendPtrRecordTo(TxMessage &aResponse, uint16_t aServic message = &aResponse.SelectMessageFor(kAnswerSection); ptr.Init(); - ptr.SetTtl(mServicesPtr.GetTtl()); + if (aResponse.GetType() == TxMessage::kLegacyUnicastResponse) + { + ptr.SetTtl(Min(Core::RecordInfo::kMaxLegacyUnicastTtl, mServicesPtr.GetTtl())); + } + else + { + ptr.SetTtl(mServicesPtr.GetTtl()); + } aResponse.AppendServicesDnssdName(kAnswerSection); offset = message->GetLength(); @@ -2792,19 +2827,19 @@ void Core::ServiceType::AppendPtrRecordTo(TxMessage &aResponse, uint16_t aServic //---------------------------------------------------------------------------------------------------------------------- // Core::TxMessage -Core::TxMessage::TxMessage(Instance &aInstance, Type aType) +Core::TxMessage::TxMessage(Instance &aInstance, Type aType, uint16_t aQueryId) : InstanceLocator(aInstance) { - Init(aType); + Init(aType, aQueryId); } -Core::TxMessage::TxMessage(Instance &aInstance, Type aType, const AddressInfo &aUnicastDest) - : TxMessage(aInstance, aType) +Core::TxMessage::TxMessage(Instance &aInstance, Type aType, const AddressInfo &aUnicastDest, uint16_t aQueryId) + : TxMessage(aInstance, aType, aQueryId) { mUnicastDest = aUnicastDest; } -void Core::TxMessage::Init(Type aType) +void Core::TxMessage::Init(Type aType, uint16_t aMessageId) { Header header; @@ -2837,7 +2872,9 @@ void Core::TxMessage::Init(Type aType) break; case kMulticastResponse: case kUnicastResponse: + case kLegacyUnicastResponse: header.SetType(Header::kTypeResponse); + header.SetMessageId(aMessageId); break; } @@ -2864,9 +2901,9 @@ Message &Core::TxMessage::SelectMessageFor(Section aSection) mainSection = kQuestionSection; extraSection = kAnswerSection; break; - - case kMulticastResponse: + case kLegacyUnicastResponse: case kUnicastResponse: + case kMulticastResponse: break; } @@ -3022,6 +3059,16 @@ void Core::TxMessage::AppendServicesDnssdName(Section aSection) return; } +void Core::TxMessage::AddQuestionFrom(const Message &aMessage) +{ + uint16_t offset = sizeof(Header); + + IgnoreError(Name::ParseName(aMessage, offset)); + offset += sizeof(ot::Dns::Question); + SuccessOrAssert(mMsgPtr->AppendBytesFromMessage(aMessage, sizeof(Header), offset - sizeof(Header))); + IncrementRecordCount(kQuestionSection); +} + void Core::TxMessage::SaveOffset(uint16_t &aCompressOffset, const Message &aMessage, Section aSection) { // Saves the current message offset in `aCompressOffset` for name @@ -3147,6 +3194,7 @@ void Core::TxMessage::Send(void) break; case kUnicastResponse: + case kLegacyUnicastResponse: otPlatMdnsSendUnicast(&GetInstance(), mMsgPtr.Release(), &mUnicastDest); break; } @@ -3215,6 +3263,8 @@ void Core::TxMessage::Reinit(void) // compress offset since the host name should not be used // in any other query question. + break; + case kLegacyUnicastResponse: break; } } @@ -3297,11 +3347,12 @@ Error Core::RxMessage::Init(Instance &aInstance, if (aSenderAddress.mPort != kUdpPort) { - if (mIsQuery) + // Simple DNS resolver does not allow more than one question in a query message + if (mIsQuery && header.GetQuestionCount() == 1) { // Section 6.7 Legacy Unicast - LogInfo("We do not yet support legacy unicast message (source port not matching mDNS port)"); - ExitNow(error = kErrorNotCapable); + mIsLegacyUnicast = true; + mQueryId = header.GetMessageId(); } else { @@ -3427,13 +3478,18 @@ Core::RxMessage::ProcessOutcome Core::RxMessage::ProcessQuery(bool aShouldProces { canAnswer = true; - if (question.mUnicastResponse) + if (question.mUnicastResponse || mIsLegacyUnicast) { needUnicastResponse = true; } } } + if (mIsLegacyUnicast) + { + shouldDelay = false; + } + VerifyOrExit(canAnswer); if (mTruncated && !aShouldProcessTruncated) @@ -3569,10 +3625,11 @@ void Core::RxMessage::AnswerQuestion(const Question &aQuestion, TimeMilli aAnswe VerifyOrExit(aQuestion.mCanAnswer); - answerInfo.mQuestionRrType = aQuestion.mRrType; - answerInfo.mAnswerTime = aAnswerTime; - answerInfo.mIsProbe = aQuestion.mIsProbe; - answerInfo.mUnicastResponse = aQuestion.mUnicastResponse; + answerInfo.mQuestionRrType = aQuestion.mRrType; + answerInfo.mAnswerTime = aAnswerTime; + answerInfo.mIsProbe = aQuestion.mIsProbe; + answerInfo.mUnicastResponse = aQuestion.mUnicastResponse; + answerInfo.mLegacyUnicastResponse = mIsLegacyUnicast; if (aQuestion.mIsForAllServicesDnssd) { @@ -3790,7 +3847,17 @@ bool Core::RxMessage::ShouldSuppressKnownAnswer(const Question &aQuestion, const void Core::RxMessage::SendUnicastResponse(const AddressInfo &aUnicastDest) { - TxMessage response(GetInstance(), TxMessage::kUnicastResponse, aUnicastDest); + TxMessage response(GetInstance(), + mIsLegacyUnicast ? TxMessage::kLegacyUnicastResponse : TxMessage::kUnicastResponse, aUnicastDest, + mIsLegacyUnicast ? mQueryId : 0); + + if (mIsLegacyUnicast) + { + // RFC6762, section 6.7: + // Legacy Unicast Response must repeat the question + response.AddQuestionFrom(*mMessagePtr); + } + TimeMilli now = TimerMilli::GetNow(); for (HostEntry &entry : Get().mHostEntries) @@ -4044,11 +4111,10 @@ void Core::MultiPacketRxMessages::AddNew(OwnedPtr &aRxMessagePtr) void Core::MultiPacketRxMessages::HandleTimer(void) { - TimeMilli now = TimerMilli::GetNow(); - TimeMilli nextTime = now.GetDistantFuture(); + NextFireTime nextTime; OwningList expiredEntries; - mRxMsgEntries.RemoveAllMatching(ExpireChecker(now), expiredEntries); + mRxMsgEntries.RemoveAllMatching(ExpireChecker(nextTime.GetNow()), expiredEntries); for (RxMsgEntry &expiredEntry : expiredEntries) { @@ -4057,13 +4123,10 @@ void Core::MultiPacketRxMessages::HandleTimer(void) for (const RxMsgEntry &msgEntry : mRxMsgEntries) { - nextTime = Min(nextTime, msgEntry.mProcessTime); + nextTime.UpdateIfEarlier(msgEntry.mProcessTime); } - if (nextTime != now.GetDistantFuture()) - { - mTimer.FireAtIfEarlier(nextTime); - } + mTimer.FireAtIfEarlier(nextTime); } void Core::MultiPacketRxMessages::Clear(void) @@ -4187,20 +4250,16 @@ void Core::TxMessageHistory::CalculateHash(const Message &aMessage, Hash &aHash) void Core::TxMessageHistory::HandleTimer(void) { - TimeMilli now = TimerMilli::GetNow(); - TimeMilli nextTime = now.GetDistantFuture(); + NextFireTime nextTime; - mHashEntries.RemoveAndFreeAllMatching(ExpireChecker(now)); + mHashEntries.RemoveAndFreeAllMatching(ExpireChecker(nextTime.GetNow())); for (const HashEntry &entry : mHashEntries) { - nextTime = Min(nextTime, entry.mExpireTime); + nextTime.UpdateIfEarlier(entry.mExpireTime); } - if (nextTime != now.GetDistantFuture()) - { - mTimer.FireAtIfEarlier(nextTime); - } + mTimer.FireAtIfEarlier(nextTime); } template diff --git a/src/core/net/mdns.hpp b/src/core/net/mdns.hpp index 1065bf160..5a8ede1ec 100644 --- a/src/core/net/mdns.hpp +++ b/src/core/net/mdns.hpp @@ -759,11 +759,12 @@ class Core : public InstanceLocator, private NonCopyable static constexpr uint32_t kMaxInitialQueryDelay = 120; // msec static constexpr uint32_t kRandomDelayReuseInterval = 2; // msec - static constexpr uint32_t kUnspecifiedTtl = 0; - static constexpr uint32_t kDefaultTtl = 120; - static constexpr uint32_t kDefaultKeyTtl = kDefaultTtl; - static constexpr uint32_t kNsecTtl = 4500; - static constexpr uint32_t kServicesPtrTtl = 4500; + static constexpr uint32_t kUnspecifiedTtl = 0; + static constexpr uint32_t kDefaultTtl = 120; + static constexpr uint32_t kDefaultKeyTtl = kDefaultTtl; + static constexpr uint32_t kLegacyUnicastNsecTtl = 10; + static constexpr uint32_t kNsecTtl = 4500; + static constexpr uint32_t kServicesPtrTtl = 4500; static constexpr uint16_t kClassQuestionUnicastFlag = (1U << 15); static constexpr uint16_t kClassCacheFlushFlag = (1U << 15); @@ -858,6 +859,7 @@ class Core : public InstanceLocator, private NonCopyable TimeMilli mAnswerTime; bool mIsProbe; bool mUnicastResponse; + bool mLegacyUnicastResponse; }; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -896,16 +898,19 @@ class Core : public InstanceLocator, private NonCopyable public: // Keeps track of record state and timings. + static constexpr uint32_t kMaxLegacyUnicastTtl = 10; // seconds + RecordInfo(void) { Clear(); } - bool IsPresent(void) const { return mIsPresent; } - uint32_t GetTtl(void) const { return mTtl; } + bool IsPresent(void) const { return mIsPresent; } template void UpdateProperty(UintType &aProperty, UintType aValue); void UpdateProperty(AddressArray &aAddrProperty, const Ip6::Address *aAddrs, uint16_t aNumAddrs); void UpdateProperty(Heap::String &aStringProperty, const char *aString); void UpdateProperty(Heap::Data &aDataProperty, const uint8_t *aData, uint16_t aLength); - void UpdateTtl(uint32_t aTtl); + + uint32_t GetTtl(bool aIsLegacyUnicast = false) const; + void UpdateTtl(uint32_t aTtl); void StartAnnouncing(void); bool ShouldAppendTo(TxMessage &aResponse, TimeMilli aNow) const; @@ -1162,7 +1167,7 @@ class Core : public InstanceLocator, private NonCopyable void AppendPtrRecordTo(TxMessage &aTxMessage, Section aSection, SubType *aSubType = nullptr); void AppendKeyRecordTo(TxMessage &aTxMessage, Section aSection); void AppendNsecRecordTo(TxMessage &aTxMessage, Section aSection); - void AppendServiceNameTo(TxMessage &TxMessage, Section aSection); + void AppendServiceNameTo(TxMessage &TxMessage, Section aSection, bool aPerformNameCompression = true); void AppendServiceTypeTo(TxMessage &aTxMessage, Section aSection); void AppendSubServiceTypeTo(TxMessage &aTxMessage, Section aSection); void AppendSubServiceNameTo(TxMessage &aTxMessage, Section aSection, SubType &aSubType); @@ -1239,10 +1244,11 @@ class Core : public InstanceLocator, private NonCopyable kMulticastQuery, kMulticastResponse, kUnicastResponse, + kLegacyUnicastResponse, }; - TxMessage(Instance &aInstance, Type aType); - TxMessage(Instance &aInstance, Type aType, const AddressInfo &aUnicastDest); + TxMessage(Instance &aInstance, Type aType, uint16_t aQueryId = 0); + TxMessage(Instance &aInstance, Type aType, const AddressInfo &aUnicastDest, uint16_t aQueryId = 0); Type GetType(void) const { return mType; } Message &SelectMessageFor(Section aSection); AppendOutcome AppendLabel(Section aSection, const char *aLabel, uint16_t &aCompressOffset); @@ -1250,6 +1256,7 @@ class Core : public InstanceLocator, private NonCopyable void AppendServiceType(Section aSection, const char *aServiceType, uint16_t &aCompressOffset); void AppendDomainName(Section aSection); void AppendServicesDnssdName(Section aSection); + void AddQuestionFrom(const Message &aMessage); void IncrementRecordCount(Section aSection) { mRecordCounts.Increment(aSection); } void CheckSizeLimitToPrepareAgain(bool &aPrepareAgain); void SaveCurrentState(void); @@ -1259,7 +1266,7 @@ class Core : public InstanceLocator, private NonCopyable private: static constexpr bool kIsSingleLabel = true; - void Init(Type aType); + void Init(Type aType, uint16_t aMessageId = 0); void Reinit(void); bool IsOverSizeLimit(void) const; AppendOutcome AppendLabels(Section aSection, @@ -1382,8 +1389,10 @@ class Core : public InstanceLocator, private NonCopyable AddressInfo mSenderAddress; RecordCounts mRecordCounts; uint16_t mStartOffset[kNumSections]; + uint16_t mQueryId; bool mIsQuery : 1; bool mIsUnicast : 1; + bool mIsLegacyUnicast : 1; bool mTruncated : 1; bool mIsSelfOriginating : 1; }; @@ -1984,7 +1993,9 @@ class Core : public InstanceLocator, private NonCopyable static uint32_t DetermineTtl(uint32_t aTtl, uint32_t aDefaultTtl); static bool NameMatch(const Heap::String &aHeapString, const char *aName); static bool NameMatch(const Heap::String &aFirst, const Heap::String &aSecond); - static void UpdateCacheFlushFlagIn(ResourceRecord &aResourceRecord, Section aSection); + static void UpdateCacheFlushFlagIn(ResourceRecord &aResourceRecord, + Section aSection, + bool aIsLegacyUnicast = false); static void UpdateRecordLengthInMessage(ResourceRecord &aRecord, Message &aMessage, uint16_t aOffset); static void UpdateCompressOffset(uint16_t &aOffset, uint16_t aNewOffse); static bool QuestionMatches(uint16_t aQuestionRrType, uint16_t aRrType); diff --git a/src/core/net/nd_agent.cpp b/src/core/net/nd_agent.cpp index e05dd283f..1af8b58c5 100644 --- a/src/core/net/nd_agent.cpp +++ b/src/core/net/nd_agent.cpp @@ -101,10 +101,10 @@ void Agent::UpdateService(void) if (error == kErrorNone) { - uint16_t rloc = Mle::kAloc16NeighborDiscoveryAgentStart + lowpanContext.mContextId - 1; + uint16_t aloc16 = Mle::kAloc16NeighborDiscoveryAgentStart + lowpanContext.mContextId - 1; mAloc.InitAsThreadOrigin(); - mAloc.GetAddress().SetToAnycastLocator(Get().GetMeshLocalPrefix(), rloc); + mAloc.GetAddress().SetToAnycastLocator(Get().GetMeshLocalPrefix(), aloc16); mAloc.mMeshLocal = true; Get().AddUnicastAddress(mAloc); ExitNow(); diff --git a/src/core/net/netif.cpp b/src/core/net/netif.cpp index 3e103312b..0701436c1 100644 --- a/src/core/net/netif.cpp +++ b/src/core/net/netif.cpp @@ -85,7 +85,6 @@ const otNetifMulticastAddress Netif::kLinkLocalAllRoutersMulticastAddress = { Netif::Netif(Instance &aInstance) : InstanceLocator(aInstance) - , mMulticastPromiscuous(false) { } @@ -454,7 +453,7 @@ Error Netif::AddExternalUnicastAddress(const UnicastAddress &aAddress) ExitNow(); } - VerifyOrExit(!aAddress.GetAddress().IsLinkLocal(), error = kErrorInvalidArgs); + VerifyOrExit(!aAddress.GetAddress().IsLinkLocalUnicast(), error = kErrorInvalidArgs); entry = mExtUnicastAddressPool.Allocate(); VerifyOrExit(entry != nullptr, error = kErrorNoBufs); diff --git a/src/core/net/netif.hpp b/src/core/net/netif.hpp index a736df8e4..4428a4776 100644 --- a/src/core/net/netif.hpp +++ b/src/core/net/netif.hpp @@ -612,23 +612,6 @@ class Netif : public InstanceLocator, private NonCopyable */ void UnsubscribeAllExternalMulticastAddresses(void); - /** - * Checks if multicast promiscuous mode is enabled on the network interface. - * - * @retval TRUE If the multicast promiscuous mode is enabled. - * @retval FALSE If the multicast promiscuous mode is disabled. - * - */ - bool IsMulticastPromiscuousEnabled(void) const { return mMulticastPromiscuous; } - - /** - * Enables multicast promiscuous mode on the network interface. - * - * @param[in] aEnabled TRUE if Multicast Promiscuous mode is enabled, FALSE otherwise. - * - */ - void SetMulticastPromiscuous(bool aEnabled) { mMulticastPromiscuous = aEnabled; } - /** * Enables range-based `for` loop iteration over external multicast addresses on the Netif that matches * a given IPv6 address type filter. @@ -702,7 +685,6 @@ class Netif : public InstanceLocator, private NonCopyable LinkedList mUnicastAddresses; LinkedList mMulticastAddresses; - bool mMulticastPromiscuous; Callback mAddressCallback; diff --git a/src/core/net/sntp_client.cpp b/src/core/net/sntp_client.cpp index 3950f49b1..7e28ad795 100644 --- a/src/core/net/sntp_client.cpp +++ b/src/core/net/sntp_client.cpp @@ -51,7 +51,7 @@ namespace Sntp { RegisterLogModule("SntpClnt"); Client::Client(Instance &aInstance) - : mSocket(aInstance) + : mSocket(aInstance, *this) , mRetransmissionTimer(aInstance) , mUnixEra(0) { @@ -61,7 +61,7 @@ Error Client::Start(void) { Error error; - SuccessOrExit(error = mSocket.Open(&Client::HandleUdpReceive, this)); + SuccessOrExit(error = mSocket.Open()); SuccessOrExit(error = mSocket.Bind(0, Ip6::kNetifUnspecified)); exit: @@ -263,11 +263,6 @@ void Client::HandleRetransmissionTimer(void) mRetransmissionTimer.FireAt(nextTime); } -void Client::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo)); -} - void Client::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { OT_UNUSED_VARIABLE(aMessageInfo); diff --git a/src/core/net/sntp_client.hpp b/src/core/net/sntp_client.hpp index d6d4dad3c..dd4c4ab33 100644 --- a/src/core/net/sntp_client.hpp +++ b/src/core/net/sntp_client.hpp @@ -272,14 +272,12 @@ class Client : private NonCopyable void FinalizeSntpTransaction(Message &aQuery, const QueryMetadata &aQueryMetadata, uint64_t aTime, Error aResult); void HandleRetransmissionTimer(void); + void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - - using RetxTimer = TimerMilliIn; - - Ip6::Udp::Socket mSocket; + using RetxTimer = TimerMilliIn; + using ClientSocket = Ip6::Udp::SocketIn; + ClientSocket mSocket; MessageQueue mPendingQueries; RetxTimer mRetransmissionTimer; diff --git a/src/core/net/srp_advertising_proxy.cpp b/src/core/net/srp_advertising_proxy.cpp index c8acc6f70..e8b086cbd 100644 --- a/src/core/net/srp_advertising_proxy.cpp +++ b/src/core/net/srp_advertising_proxy.cpp @@ -944,7 +944,7 @@ void AdvertisingProxy::RegisterHost(Host &aHost) for (const Ip6::Address &address : aHost.mAddresses) { - if (!address.IsLinkLocal() && !Get().IsMeshLocalAddress(address)) + if (!address.IsLinkLocalUnicast() && !Get().IsMeshLocalAddress(address)) { IgnoreError(hostAddresses.PushBack(address)); } diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index da697968c..ebc28ce51 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -68,13 +68,19 @@ void Client::HostInfo::Clear(void) SetState(kRemoved); } -void Client::HostInfo::SetState(ItemState aState) +bool Client::HostInfo::SetState(ItemState aState) { - if (aState != GetState()) - { - LogInfo("HostInfo %s -> %s", ItemStateToString(GetState()), ItemStateToString(aState)); - mState = MapEnum(aState); - } + bool didChange; + + VerifyOrExit(aState != GetState(), didChange = false); + + LogInfo("HostInfo %s -> %s", ItemStateToString(GetState()), ItemStateToString(aState)); + + mState = MapEnum(aState); + didChange = true; + +exit: + return didChange; } void Client::HostInfo::EnableAutoAddress(void) @@ -121,9 +127,11 @@ Error Client::Service::Init(void) return error; } -void Client::Service::SetState(ItemState aState) +bool Client::Service::SetState(ItemState aState) { - VerifyOrExit(GetState() != aState); + bool didChange; + + VerifyOrExit(GetState() != aState, didChange = false); LogInfo("Service %s -> %s, \"%s\" \"%s\"", ItemStateToString(GetState()), ItemStateToString(aState), GetInstanceName(), GetName()); @@ -150,10 +158,11 @@ void Client::Service::SetState(ItemState aState) GetPriority(), GetNumTxtEntries()); } - mState = MapEnum(aState); + mState = MapEnum(aState); + didChange = true; exit: - return; + return didChange; } bool Client::Service::Matches(const Service &aOther) const @@ -249,7 +258,8 @@ Client::Client(Instance &aInstance) , mServiceKeyRecordEnabled(false) , mUseShortLeaseOption(false) #endif - , mUpdateMessageId(0) + , mNextMessageId(0) + , mResponseMessageId(0) , mAutoHostAddressCount(0) , mRetryWaitInterval(kMinRetryWaitInterval) , mTtl(0) @@ -257,7 +267,7 @@ Client::Client(Instance &aInstance) , mKeyLease(0) , mDefaultLease(kDefaultLease) , mDefaultKeyLease(kDefaultKeyLease) - , mSocket(aInstance) + , mSocket(aInstance, *this) , mDomainName(kDefaultDomainName) , mTimer(aInstance) { @@ -286,7 +296,7 @@ Error Client::Start(const Ip6::SockAddr &aServerSockAddr, Requester aRequester) VerifyOrExit(GetState() == kStateStopped, error = (aServerSockAddr == GetServerAddress()) ? kErrorNone : kErrorBusy); - SuccessOrExit(error = mSocket.Open(Client::HandleUdpReceive, this)); + SuccessOrExit(error = mSocket.Open()); SuccessOrExit(error = mSocket.Connect(aServerSockAddr)); LogInfo("%starting, server %s", (aRequester == kRequesterUser) ? "S" : "Auto-s", @@ -345,6 +355,7 @@ void Client::Stop(Requester aRequester, StopMode aMode) mShouldRemoveKeyLease = false; mTxFailureRetryCount = 0; + mResponseMessageId = mNextMessageId; if (aMode == kResetRetryInterval) { @@ -517,15 +528,15 @@ bool Client::ShouldUpdateHostAutoAddresses(void) const { bool shouldUpdate = false; uint16_t registeredCount = 0; - Ip6::Netif::UnicastAddress &ml64 = Get().GetMeshLocal64UnicastAddress(); + Ip6::Netif::UnicastAddress &mlEid = Get().GetMeshLocalEidUnicastAddress(); VerifyOrExit(mHostInfo.IsAutoAddressEnabled()); // Check all addresses on `ThreadNetif` excluding the mesh local - // EID (`ml64`). If any address should be registered but is not, + // EID (`mlEid`). 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` + // the addresses. If there is no eligible address, then `mlEid` // 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 @@ -533,7 +544,7 @@ bool Client::ShouldUpdateHostAutoAddresses(void) const for (const Ip6::Netif::UnicastAddress &unicastAddress : Get().GetUnicastAddresses()) { - if (&unicastAddress == &ml64) + if (&unicastAddress == &mlEid) { continue; } @@ -551,7 +562,7 @@ bool Client::ShouldUpdateHostAutoAddresses(void) const if (registeredCount == 0) { - ExitNow(shouldUpdate = !ml64.mSrpRegistered); + ExitNow(shouldUpdate = !mlEid.mSrpRegistered); } shouldUpdate = (registeredCount != mAutoHostAddressCount); @@ -566,7 +577,7 @@ bool Client::ShouldHostAutoAddressRegister(const Ip6::Netif::UnicastAddress &aUn VerifyOrExit(aUnicastAddress.mValid); VerifyOrExit(aUnicastAddress.mPreferred); - VerifyOrExit(!aUnicastAddress.GetAddress().IsLinkLocal()); + VerifyOrExit(!aUnicastAddress.GetAddress().IsLinkLocalUnicast()); VerifyOrExit(!Get().IsMeshLocalAddress(aUnicastAddress.GetAddress())); shouldRegister = true; @@ -739,13 +750,15 @@ void Client::SetState(State aState) return; } -void Client::ChangeHostAndServiceStates(const ItemState *aNewStates, ServiceStateChangeMode aMode) +bool Client::ChangeHostAndServiceStates(const ItemState *aNewStates, ServiceStateChangeMode aMode) { + bool anyChanged; + #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE ItemState oldHostState = mHostInfo.GetState(); #endif - mHostInfo.SetState(aNewStates[mHostInfo.GetState()]); + anyChanged = mHostInfo.SetState(aNewStates[mHostInfo.GetState()]); for (Service &service : mServices) { @@ -754,7 +767,7 @@ void Client::ChangeHostAndServiceStates(const ItemState *aNewStates, ServiceStat continue; } - service.SetState(aNewStates[service.GetState()]); + anyChanged |= service.SetState(aNewStates[service.GetState()]); } #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE @@ -781,6 +794,8 @@ void Client::ChangeHostAndServiceStates(const ItemState *aNewStates, ServiceStat } } #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE + + return anyChanged; } void Client::InvokeCallback(Error aError) const { InvokeCallback(aError, mHostInfo, nullptr); } @@ -806,6 +821,7 @@ void Client::SendUpdate(void) Error error = kErrorNone; Message *message = mSocket.NewMessage(); uint32_t length; + bool anyChanged; VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = PrepareUpdateMessage(*message)); @@ -822,14 +838,35 @@ void Client::SendUpdate(void) SuccessOrExit(error = mSocket.SendTo(*message, Ip6::MessageInfo())); - LogInfo("Send update"); + LogInfo("Send update, msg-id:0x%x", mNextMessageId); // State changes: // kToAdd -> kAdding // kToRefresh -> kRefreshing // kToRemove -> kRemoving - ChangeHostAndServiceStates(kNewStateOnMessageTx, kForServicesAppendedInMessage); + anyChanged = ChangeHostAndServiceStates(kNewStateOnMessageTx, kForServicesAppendedInMessage); + + // `mNextMessageId` tracks the message ID used in the prepared + // update message. It is incremented after a successful + // `mSocket.SendTo()` call. If unsuccessful, the same ID can be + // reused for the next update. + // + // Acceptable response message IDs fall within the range starting + // at `mResponseMessageId ` and ending before `mNextMessageId`. + // + // `anyChanged` tracks if any host or service states have changed. + // If not, the prepared message is identical to the last one with + // the same hosts/services, allowing us to accept earlier message + // IDs. If changes occur, `mResponseMessageId ` is updated to + // ensure only responses to the latest message are accepted. + + if (anyChanged) + { + mResponseMessageId = mNextMessageId; + } + + mNextMessageId++; // Remember the update message tx time to use later to determine the // lease renew time. @@ -902,13 +939,7 @@ Error Client::PrepareUpdateMessage(Message &aMessage) SuccessOrExit(error = ReadOrGenerateKey(info.mKeyPair)); #endif - // Generate random Message ID and ensure it is different from last one - do - { - SuccessOrExit(error = header.SetRandomMessageId()); - } while (header.GetMessageId() == mUpdateMessageId); - - mUpdateMessageId = header.GetMessageId(); + header.SetMessageId(mNextMessageId); // SRP Update (DNS Update) message must have exactly one record in // Zone section, no records in Prerequisite Section, can have @@ -1319,10 +1350,10 @@ Error Client::AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo) if (mAutoHostAddressCount == 0) { - Ip6::Netif::UnicastAddress &ml64 = Get().GetMeshLocal64UnicastAddress(); + Ip6::Netif::UnicastAddress &mlEid = Get().GetMeshLocalEidUnicastAddress(); - SuccessOrExit(error = AppendAaaaRecord(ml64.GetAddress(), aMessage, aInfo)); - ml64.mSrpRegistered = true; + SuccessOrExit(error = AppendAaaaRecord(mlEid.GetAddress(), aMessage, aInfo)); + mlEid.mSrpRegistered = true; mAutoHostAddressCount++; } } @@ -1552,11 +1583,11 @@ void Client::UpdateRecordLengthInMessage(Dns::ResourceRecord &aRecord, uint16_t aMessage.Write(aOffset, aRecord); } -void Client::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +void Client::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { OT_UNUSED_VARIABLE(aMessageInfo); - static_cast(aContext)->ProcessResponse(AsCoreType(aMessage)); + ProcessResponse(aMessage); } void Client::ProcessResponse(Message &aMessage) @@ -1578,22 +1609,32 @@ void Client::ProcessResponse(Message &aMessage) uint16_t recordCount; LinkedList removedServices; - VerifyOrExit(GetState() == kStateUpdating); + switch (GetState()) + { + case kStateToUpdate: + case kStateUpdating: + case kStateToRetry: + break; + case kStateStopped: + case kStatePaused: + case kStateUpdated: + ExitNow(); + } SuccessOrExit(error = aMessage.Read(offset, header)); VerifyOrExit(header.GetType() == Dns::Header::kTypeResponse, error = kErrorParse); VerifyOrExit(header.GetQueryType() == Dns::Header::kQueryTypeUpdate, error = kErrorParse); - VerifyOrExit(header.GetMessageId() == mUpdateMessageId, error = kErrorDrop); + + VerifyOrExit(IsResponseMessageIdValid(header.GetMessageId()), error = kErrorDrop); + mResponseMessageId = header.GetMessageId() + 1; if (!Get().IsRxOnWhenIdle()) { Get().StopFastPolls(); } - // Response is for the earlier request message. - - LogInfo("Received response"); + LogInfo("Received response, msg-id:0x%x", header.GetMessageId()); #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE mAutoStart.ResetTimeoutFailureCount(); @@ -1722,6 +1763,13 @@ void Client::ProcessResponse(Message &aMessage) } } +bool Client::IsResponseMessageIdValid(uint16_t aId) const +{ + // Semantically equivalent to `(aId >= mResponseMessageId) && (aId < mNextMessageId)` + + return !SerialNumber::IsLess(aId, mResponseMessageId) && SerialNumber::IsLess(aId, mNextMessageId); +} + void Client::HandleUpdateDone(void) { HostInfo hostInfoCopy = mHostInfo; diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp index 32b1ea0de..f45b95364 100644 --- a/src/core/net/srp_client.hpp +++ b/src/core/net/srp_client.hpp @@ -167,7 +167,7 @@ class Client : public InstanceLocator, private NonCopyable private: void SetName(const char *aName) { mName = aName; } - void SetState(ItemState aState); + bool SetState(ItemState aState); void SetAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddresses); void EnableAutoAddress(void); }; @@ -303,7 +303,7 @@ class Client : public InstanceLocator, private NonCopyable static constexpr uint32_t kAppendedInMsgFlag = (1U << 31); static constexpr uint32_t kLeaseMask = ~kAppendedInMsgFlag; - void SetState(ItemState aState); + bool SetState(ItemState aState); TimeMilli GetLeaseRenewTime(void) const { return TimeMilli(mData); } void SetLeaseRenewTime(TimeMilli aTime) { mData = aTime.GetValue(); } bool IsAppendedInMessage(void) const { return mLease & kAppendedInMsgFlag; } @@ -788,6 +788,17 @@ class Client : public InstanceLocator, private NonCopyable * */ bool GetUseShortLeaseOption(void) const { return mUseShortLeaseOption; } + + /** + * Set the next DNS message ID for client to use. + * + * This is intended for testing only. + * + * @pram[in] aMessageId A message ID. + * + */ + void SetNextMessageId(uint16_t aMessageId) { mNextMessageId = aMessageId; } + #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE private: @@ -1007,7 +1018,7 @@ class Client : public InstanceLocator, private NonCopyable void UpdateServiceStateToRemove(Service &aService); State GetState(void) const { return mState; } void SetState(State aState); - void ChangeHostAndServiceStates(const ItemState *aNewStates, ServiceStateChangeMode aMode); + bool ChangeHostAndServiceStates(const ItemState *aNewStates, ServiceStateChangeMode aMode); void InvokeCallback(Error aError) const; void InvokeCallback(Error aError, const HostInfo &aHostInfo, const Service *aRemovedServices) const; void HandleHostInfoOrServiceChange(void); @@ -1029,8 +1040,9 @@ class Client : public InstanceLocator, private NonCopyable Error AppendUpdateLeaseOptRecord(Message &aMessage); Error AppendSignature(Message &aMessage, Info &aInfo); void UpdateRecordLengthInMessage(Dns::ResourceRecord &aRecord, uint16_t aOffset, Message &aMessage) const; - static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); + void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void ProcessResponse(Message &aMessage); + bool IsResponseMessageIdValid(uint16_t aId) const; void HandleUpdateDone(void); void GetRemovedServices(LinkedList &aRemovedServices); static Error ReadResourceRecord(const Message &aMessage, uint16_t &aOffset, Dns::ResourceRecord &aRecord); @@ -1062,7 +1074,8 @@ class Client : public InstanceLocator, private NonCopyable static_assert(kMaxTxFailureRetries < 16, "kMaxTxFailureRetries exceed the range of mTxFailureRetryCount (4-bit)"); - using DelayTimer = TimerMilliIn; + using DelayTimer = TimerMilliIn; + using ClientSocket = Ip6::Udp::SocketIn; State mState; uint8_t mTxFailureRetryCount : 4; @@ -1073,7 +1086,8 @@ class Client : public InstanceLocator, private NonCopyable bool mUseShortLeaseOption : 1; #endif - uint16_t mUpdateMessageId; + uint16_t mNextMessageId; + uint16_t mResponseMessageId; uint16_t mAutoHostAddressCount; uint32_t mRetryWaitInterval; @@ -1084,7 +1098,7 @@ class Client : public InstanceLocator, private NonCopyable uint32_t mDefaultLease; uint32_t mDefaultKeyLease; - Ip6::Udp::Socket mSocket; + ClientSocket mSocket; Callback mCallback; const char *mDomainName; diff --git a/src/core/net/srp_server.cpp b/src/core/net/srp_server.cpp index b3ea87b5d..c6ffe222a 100644 --- a/src/core/net/srp_server.cpp +++ b/src/core/net/srp_server.cpp @@ -86,7 +86,7 @@ static Dns::UpdateHeader::Response ErrorToDnsResponseCode(Error aError) Server::Server(Instance &aInstance) : InstanceLocator(aInstance) - , mSocket(aInstance) + , mSocket(aInstance, *this) , mLeaseTimer(aInstance) , mOutstandingUpdatesTimer(aInstance) , mCompletedUpdateTask(aInstance) @@ -668,7 +668,7 @@ Error Server::PrepareSocket(void) #endif VerifyOrExit(!mSocket.IsOpen()); - SuccessOrExit(error = mSocket.Open(HandleUdpReceive, this)); + SuccessOrExit(error = mSocket.Open()); error = mSocket.Bind(mPort, Ip6::kNetifThread); exit: @@ -1561,11 +1561,6 @@ void Server::SendResponse(const Dns::UpdateHeader &aHeader, FreeMessageOnError(response, error); } -void Server::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo)); -} - void Server::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error = ProcessMessage(aMessage, aMessageInfo); @@ -1788,7 +1783,7 @@ void Server::UpdateAddrResolverCacheTable(const Ip6::MessageInfo &aMessageInfo, rloc16 = Get().LookUp(aMessageInfo.GetPeerAddr()); - VerifyOrExit(rloc16 != Mac::kShortAddrInvalid); + VerifyOrExit(rloc16 != Mle::kInvalidRloc16); for (const Ip6::Address &address : aHost.mAddresses) { diff --git a/src/core/net/srp_server.hpp b/src/core/net/srp_server.hpp index ebe67d8fe..48c9780a3 100644 --- a/src/core/net/srp_server.hpp +++ b/src/core/net/srp_server.hpp @@ -1034,7 +1034,6 @@ class Server : public InstanceLocator, private NonCopyable uint32_t aKeyLease, bool mUseShortLeaseOption, const Ip6::MessageInfo &aMessageInfo); - static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void HandleLeaseTimer(void); static void HandleOutstandingUpdatesTimer(Timer &aTimer); @@ -1050,8 +1049,9 @@ class Server : public InstanceLocator, private NonCopyable using LeaseTimer = TimerMilliIn; using UpdateTimer = TimerMilliIn; using CompletedUpdatesTask = TaskletIn; + using ServerSocket = Ip6::Udp::SocketIn; - Ip6::Udp::Socket mSocket; + ServerSocket mSocket; Callback mServiceUpdateHandler; diff --git a/src/core/net/udp6.cpp b/src/core/net/udp6.cpp index bb24e5030..5fd34b266 100644 --- a/src/core/net/udp6.cpp +++ b/src/core/net/udp6.cpp @@ -47,6 +47,9 @@ namespace ot { namespace Ip6 { +//--------------------------------------------------------------------------------------------------------------------- +// Udp::SocketHandle + bool Udp::SocketHandle::Matches(const MessageInfo &aMessageInfo) const { bool matches = false; @@ -71,10 +74,15 @@ bool Udp::SocketHandle::Matches(const MessageInfo &aMessageInfo) const return matches; } -Udp::Socket::Socket(Instance &aInstance) +//--------------------------------------------------------------------------------------------------------------------- +// Udp::Socket + +Udp::Socket::Socket(Instance &aInstance, ReceiveHandler aHandler, void *aContext) : InstanceLocator(aInstance) { Clear(); + mHandler = aHandler; + mContext = aContext; } Message *Udp::Socket::NewMessage(void) { return NewMessage(0); } @@ -86,7 +94,7 @@ Message *Udp::Socket::NewMessage(uint16_t aReserved, const Message::Settings &aS return Get().NewMessage(aReserved, aSettings); } -Error Udp::Socket::Open(otUdpReceive aHandler, void *aContext) { return Get().Open(*this, aHandler, aContext); } +Error Udp::Socket::Open(void) { return Get().Open(*this, mHandler, mContext); } bool Udp::Socket::IsOpen(void) const { return Get().IsOpen(*this); } @@ -147,6 +155,9 @@ Error Udp::Socket::LeaveNetifMulticastGroup(NetifIdentifier aNetifIdentifier, co } #endif +//--------------------------------------------------------------------------------------------------------------------- +// Udp + Udp::Udp(Instance &aInstance) : InstanceLocator(aInstance) , mEphemeralPort(kDynamicPortMin) @@ -169,14 +180,13 @@ Error Udp::RemoveReceiver(Receiver &aReceiver) return error; } -Error Udp::Open(SocketHandle &aSocket, otUdpReceive aHandler, void *aContext) +Error Udp::Open(SocketHandle &aSocket, ReceiveHandler aHandler, void *aContext) { Error error = kErrorNone; OT_ASSERT(!IsOpen(aSocket)); - aSocket.GetSockName().Clear(); - aSocket.GetPeerName().Clear(); + aSocket.Clear(); aSocket.mHandler = aHandler; aSocket.mContext = aContext; diff --git a/src/core/net/udp6.hpp b/src/core/net/udp6.hpp index 24e706607..73d7429fc 100644 --- a/src/core/net/udp6.hpp +++ b/src/core/net/udp6.hpp @@ -84,6 +84,8 @@ enum NetifIdentifier : uint8_t class Udp : public InstanceLocator, private NonCopyable { public: + typedef otUdpReceive ReceiveHandler; ///< Receive handler callback. + /** * Implements a UDP/IPv6 socket. * @@ -157,9 +159,11 @@ class Udp : public InstanceLocator, private NonCopyable * Initializes the object. * * @param[in] aInstance A reference to OpenThread instance. + * @param[in] aHandler A pointer to a function that is called when receiving UDP messages. + * @param[in] aContext A pointer to arbitrary context information. * */ - explicit Socket(Instance &aInstance); + Socket(Instance &aInstance, ReceiveHandler aHandler, void *aContext); /** * Returns a new UDP message with default settings (link security enabled and `kPriorityNormal`) @@ -193,14 +197,11 @@ class Udp : public InstanceLocator, private NonCopyable /** * Opens the UDP socket. * - * @param[in] aHandler A pointer to a function that is called when receiving UDP messages. - * @param[in] aContext A pointer to arbitrary context information. - * * @retval kErrorNone Successfully opened the socket. * @retval kErrorFailed Failed to open the socket. * */ - Error Open(otUdpReceive aHandler, void *aContext); + Error Open(void); /** * Returns if the UDP socket is open. @@ -324,6 +325,36 @@ class Udp : public InstanceLocator, private NonCopyable #endif }; + /** + * A socket owned by a specific type with a given owner type as the callback. + * + * @tparam Owner The type of the owner of this socket. + * @tparam HandleUdpReceivePtr A pointer to a non-static member method of `Owner` to handle received messages. + * + */ + template + class SocketIn : public Socket + { + public: + /** + * Initializes the socket. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aOnwer The owner of the socket, providing the `HandleUdpReceivePtr` callback. + * + */ + explicit SocketIn(Instance &aInstance, Owner &aOwner) + : Socket(aInstance, HandleUdpReceive, &aOwner) + { + } + + private: + static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) + { + (reinterpret_cast(aContext)->*HandleUdpReceivePtr)(AsCoreType(aMessage), AsCoreType(aMessageInfo)); + } + }; + /** * Implements a UDP receiver. * @@ -480,7 +511,7 @@ class Udp : public InstanceLocator, private NonCopyable * @retval kErrorFailed Failed to open the socket. * */ - Error Open(SocketHandle &aSocket, otUdpReceive aHandler, void *aContext); + Error Open(SocketHandle &aSocket, ReceiveHandler aHandler, void *aContext); /** * Returns if a UDP socket is open. diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp index 28f6483cd..913b184a0 100644 --- a/src/core/thread/address_resolver.cpp +++ b/src/core/thread/address_resolver.cpp @@ -176,7 +176,7 @@ AddressResolver::CacheEntry *AddressResolver::GetEntryAfter(CacheEntry *aPrev, C return (aPrev == nullptr) ? aList.GetHead() : aPrev->GetNext(); } -void AddressResolver::Remove(Mac::ShortAddress aRloc16, bool aMatchRouterId) +void AddressResolver::Remove(uint16_t aRloc16, bool aMatchRouterId) { CacheEntryList *lists[] = {&mCachedList, &mSnoopedList}; @@ -337,7 +337,7 @@ void AddressResolver::RemoveCacheEntry(CacheEntry &aEntry, LogCacheEntryChange(kEntryRemoved, aReason, aEntry, &aList); } -Error AddressResolver::UpdateCacheEntry(const Ip6::Address &aEid, Mac::ShortAddress aRloc16) +Error AddressResolver::UpdateCacheEntry(const Ip6::Address &aEid, uint16_t aRloc16) { // This method updates an existing cache entry for the EID (if any). // Returns `kErrorNone` if entry is found and successfully updated, @@ -377,18 +377,16 @@ Error AddressResolver::UpdateCacheEntry(const Ip6::Address &aEid, Mac::ShortAddr return error; } -void AddressResolver::UpdateSnoopedCacheEntry(const Ip6::Address &aEid, - Mac::ShortAddress aRloc16, - Mac::ShortAddress aDest) +void AddressResolver::UpdateSnoopedCacheEntry(const Ip6::Address &aEid, uint16_t aRloc16, uint16_t aDest) { - uint16_t numNonEvictable = 0; - CacheEntry *entry; - Mac::ShortAddress macAddress; + uint16_t numNonEvictable = 0; + CacheEntry *entry; + uint16_t deviceRloc16; VerifyOrExit(Get().IsFullThreadDevice()); #if OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES - VerifyOrExit(ResolveUsingNetDataServices(aEid, macAddress) != kErrorNone); + VerifyOrExit(ResolveUsingNetDataServices(aEid, deviceRloc16) != kErrorNone); #endif VerifyOrExit(UpdateCacheEntry(aEid, aRloc16) != kErrorNone); @@ -396,13 +394,13 @@ void AddressResolver::UpdateSnoopedCacheEntry(const Ip6::Address &aEid, // Skip if the `aRloc16` (i.e., the source of the snooped message) // is this device or an MTD (minimal) child of the device itself. - macAddress = Get().GetShortAddress(); - VerifyOrExit((aRloc16 != macAddress) && !Get().IsMinimalChild(aRloc16)); + deviceRloc16 = Get().GetRloc16(); + VerifyOrExit((aRloc16 != deviceRloc16) && !Get().HasMinimalChild(aRloc16)); // Ensure that the destination of the snooped message is this device // or a minimal child of this device. - VerifyOrExit((aDest == macAddress) || Get().IsMinimalChild(aDest)); + VerifyOrExit((aDest == deviceRloc16) || Get().HasMinimalChild(aDest)); entry = NewCacheEntry(/* aSnoopedEntry */ true); VerifyOrExit(entry != nullptr); @@ -470,15 +468,15 @@ void AddressResolver::RestartAddressQueries(void) } } -Mac::ShortAddress AddressResolver::LookUp(const Ip6::Address &aEid) +uint16_t AddressResolver::LookUp(const Ip6::Address &aEid) { - Mac::ShortAddress rloc16 = Mac::kShortAddrInvalid; + uint16_t rloc16 = Mle::kInvalidRloc16; IgnoreError(Resolve(aEid, rloc16, /* aAllowAddressQuery */ false)); return rloc16; } -Error AddressResolver::Resolve(const Ip6::Address &aEid, Mac::ShortAddress &aRloc16, bool aAllowAddressQuery) +Error AddressResolver::Resolve(const Ip6::Address &aEid, uint16_t &aRloc16, bool aAllowAddressQuery) { Error error = kErrorNone; CacheEntry *entry; @@ -532,7 +530,7 @@ Error AddressResolver::Resolve(const Ip6::Address &aEid, Mac::ShortAddress &aRlo VerifyOrExit(entry != nullptr, error = kErrorNoBufs); entry->SetTarget(aEid); - entry->SetRloc16(Mac::kShortAddrInvalid); + entry->SetRloc16(Mle::kInvalidRloc16); entry->SetRetryDelay(kAddressQueryInitialRetryDelay); entry->SetCanEvict(false); list = nullptr; @@ -580,7 +578,7 @@ Error AddressResolver::Resolve(const Ip6::Address &aEid, Mac::ShortAddress &aRlo #if OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES -Error AddressResolver::ResolveUsingNetDataServices(const Ip6::Address &aEid, Mac::ShortAddress &aRloc16) +Error AddressResolver::ResolveUsingNetDataServices(const Ip6::Address &aEid, uint16_t &aRloc16) { // Tries to resolve `aEid` Network Data DNS/SRP Unicast address // service entries. Returns `kErrorNone` and updates `aRloc16` @@ -787,7 +785,7 @@ void AddressResolver::HandleTmf(Coap::Message &aMessage, const for (Ip6::Netif::UnicastAddress &address : Get().GetUnicastAddresses()) { - if (address.GetAddress() == target && Get().GetMeshLocal64().GetIid() != meshLocalIid) + if (address.GetAddress() == target && Get().GetMeshLocalEid().GetIid() != meshLocalIid) { // Target EID matches address and Mesh Local EID differs #if OPENTHREAD_CONFIG_DUA_ENABLE @@ -822,7 +820,7 @@ void AddressResolver::HandleTmf(Coap::Message &aMessage, const if (child.RemoveIp6Address(target) == kErrorNone) { - SuccessOrExit(error = Get().GetLocatorAddress(destination, child.GetRloc16())); + destination.SetToRoutingLocator(Get().GetMeshLocalPrefix(), child.GetRloc16()); SendAddressError(target, meshLocalIid, &destination); ExitNow(); @@ -856,7 +854,7 @@ void AddressResolver::HandleTmf(Coap::Message &aMessage, const if (Get().HasUnicastAddress(target)) { - SendAddressQueryResponse(target, Get().GetMeshLocal64().GetIid(), nullptr, + SendAddressQueryResponse(target, Get().GetMeshLocalEid().GetIid(), nullptr, aMessageInfo.GetPeerAddr()); ExitNow(); } diff --git a/src/core/thread/address_resolver.hpp b/src/core/thread/address_resolver.hpp index 8b5d0a422..9e9799f19 100644 --- a/src/core/thread/address_resolver.hpp +++ b/src/core/thread/address_resolver.hpp @@ -139,7 +139,7 @@ class AddressResolver : public InstanceLocator, private NonCopyable * @param[in] aRloc16 The RLOC16 address. * */ - void RemoveEntriesForRloc16(Mac::ShortAddress aRloc16); + void RemoveEntriesForRloc16(uint16_t aRloc16); /** * Removes all EID-to-RLOC cache entries associated with a Router ID. @@ -177,7 +177,7 @@ class AddressResolver : public InstanceLocator, private NonCopyable * @param[in] aDest The short MAC address destination of the received snooped message. * */ - void UpdateSnoopedCacheEntry(const Ip6::Address &aEid, Mac::ShortAddress aRloc16, Mac::ShortAddress aDest); + void UpdateSnoopedCacheEntry(const Ip6::Address &aEid, uint16_t aRloc16, uint16_t aDest); /** * Returns the RLOC16 for a given EID, initiates an Address Query if the mapping is not known. @@ -191,7 +191,7 @@ class AddressResolver : public InstanceLocator, private NonCopyable * @retval kErrorNoBufs Insufficient buffer space available to send Address Query. * */ - Error Resolve(const Ip6::Address &aEid, Mac::ShortAddress &aRloc16) + Error Resolve(const Ip6::Address &aEid, uint16_t &aRloc16) { return Resolve(aEid, aRloc16, /* aAllowAddressQuery */ true); } @@ -204,10 +204,10 @@ class AddressResolver : public InstanceLocator, private NonCopyable * * @param[in] aEid A reference to the EID to lookup. * - * @returns The RLOC16 mapping to @p aEid or `Mac::kShortAddrInvalid` if it is not found in the address cache. + * @returns The RLOC16 mapping to @p aEid or `Mle::kInvalidRloc16` if it is not found in the address cache. * */ - Mac::ShortAddress LookUp(const Ip6::Address &aEid); + uint16_t LookUp(const Ip6::Address &aEid); /** * Restarts any ongoing address queries. @@ -267,8 +267,8 @@ class AddressResolver : public InstanceLocator, private NonCopyable const Ip6::Address &GetTarget(void) const { return mTarget; } void SetTarget(const Ip6::Address &aTarget) { mTarget = aTarget; } - Mac::ShortAddress GetRloc16(void) const { return mRloc16; } - void SetRloc16(Mac::ShortAddress aRloc16) { mRloc16 = aRloc16; } + uint16_t GetRloc16(void) const { return mRloc16; } + void SetRloc16(uint16_t aRloc16) { mRloc16 = aRloc16; } const Ip6::InterfaceIdentifier &GetMeshLocalIid(void) const { return mInfo.mCached.mMeshLocalIid; } void SetMeshLocalIid(const Ip6::InterfaceIdentifier &aIid) { mInfo.mCached.mMeshLocalIid = aIid; } @@ -298,9 +298,9 @@ class AddressResolver : public InstanceLocator, private NonCopyable static constexpr uint16_t kNoNextIndex = 0xffff; // `mNextIndex` value when at end of list. static constexpr uint32_t kInvalidLastTransTime = 0xffffffff; // Value when `mLastTransactionTime` is invalid. - Ip6::Address mTarget; - Mac::ShortAddress mRloc16; - uint16_t mNextIndex; + Ip6::Address mTarget; + uint16_t mRloc16; + uint16_t mNextIndex; union { @@ -348,16 +348,16 @@ class AddressResolver : public InstanceLocator, private NonCopyable CacheEntryPool &GetCacheEntryPool(void) { return mCacheEntryPool; } - Error Resolve(const Ip6::Address &aEid, Mac::ShortAddress &aRloc16, bool aAllowAddressQuery); - void Remove(Mac::ShortAddress aRloc16, bool aMatchRouterId); + Error Resolve(const Ip6::Address &aEid, uint16_t &aRloc16, bool aAllowAddressQuery); + void Remove(uint16_t aRloc16, bool aMatchRouterId); void Remove(const Ip6::Address &aEid, Reason aReason); CacheEntry *FindCacheEntry(const Ip6::Address &aEid, CacheEntryList *&aList, CacheEntry *&aPrevEntry); CacheEntry *NewCacheEntry(bool aSnoopedEntry); void RemoveCacheEntry(CacheEntry &aEntry, CacheEntryList &aList, CacheEntry *aPrevEntry, Reason aReason); - Error UpdateCacheEntry(const Ip6::Address &aEid, Mac::ShortAddress aRloc16); + Error UpdateCacheEntry(const Ip6::Address &aEid, uint16_t aRloc16); Error SendAddressQuery(const Ip6::Address &aEid); #if OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES - Error ResolveUsingNetDataServices(const Ip6::Address &aEid, Mac::ShortAddress &aRloc16); + Error ResolveUsingNetDataServices(const Ip6::Address &aEid, uint16_t &aRloc16); #endif static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); diff --git a/src/core/thread/anycast_locator.cpp b/src/core/thread/anycast_locator.cpp index f3fd31e90..03cf14563 100644 --- a/src/core/thread/anycast_locator.cpp +++ b/src/core/thread/anycast_locator.cpp @@ -91,7 +91,7 @@ void AnycastLocator::HandleResponse(Coap::Message *aMessage, const Ip6::MessageI { OT_UNUSED_VARIABLE(aMessageInfo); - uint16_t rloc16 = Mac::kShortAddrInvalid; + uint16_t rloc16 = Mle::kInvalidRloc16; const Ip6::Address *address = nullptr; Ip6::Address meshLocalAddress; @@ -124,7 +124,7 @@ void AnycastLocator::HandleTmf(Coap::Message &aMessage, const message = Get().NewResponseMessage(aMessage); VerifyOrExit(message != nullptr); - SuccessOrExit(Tlv::Append(*message, Get().GetMeshLocal64().GetIid())); + SuccessOrExit(Tlv::Append(*message, Get().GetMeshLocalEid().GetIid())); SuccessOrExit(Tlv::Append(*message, Get().GetRloc16())); SuccessOrExit(Get().SendMessage(*message, aMessageInfo)); diff --git a/src/core/thread/child.cpp b/src/core/thread/child.cpp index d627e175b..0f4efacd9 100644 --- a/src/core/thread/child.cpp +++ b/src/core/thread/child.cpp @@ -44,6 +44,9 @@ namespace ot { #if OPENTHREAD_FTD +//--------------------------------------------------------------------------------------------------------------------- +// Child::Info + void Child::Info::SetFrom(const Child &aChild) { Clear(); @@ -75,39 +78,50 @@ void Child::Info::SetFrom(const Child &aChild) #endif } -const Ip6::Address *Child::AddressIterator::GetAddress(void) const -{ - // `mIndex` value of zero indicates mesh-local IPv6 address. - // Non-zero value specifies the index into address array starting - // from one for first element (i.e, `mIndex - 1` gives the array - // index). +//--------------------------------------------------------------------------------------------------------------------- +// Child::Ip6AddrEntry - return (mIndex == 0) ? &mMeshLocalAddress : ((mIndex < kMaxIndex) ? &mChild.mIp6Address[mIndex - 1] : nullptr); -} +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE -void Child::AddressIterator::Update(void) +MlrState Child::Ip6AddrEntry::GetMlrState(const Child &aChild) const { - const Ip6::Address *address; + MlrState state = kMlrStateRegistering; + Ip6AddressArray::IndexType index; + + OT_ASSERT(aChild.mIp6Addresses.IsInArrayBuffer(this)); - if ((mIndex == 0) && (mChild.GetMeshLocalIp6Address(mMeshLocalAddress) != kErrorNone)) + index = aChild.mIp6Addresses.IndexOf(*this); + + if (aChild.mMlrToRegisterMask.Get(index)) { - mIndex++; + state = kMlrStateToRegister; } - - while (true) + else if (aChild.mMlrRegisteredMask.Get(index)) { - address = GetAddress(); + state = kMlrStateRegistered; + } - VerifyOrExit((address != nullptr) && !address->IsUnspecified(), mIndex = kMaxIndex); + return state; +} - VerifyOrExit(!address->MatchesFilter(mFilter)); - mIndex++; - } +// NOLINTNEXTLINE(readability-make-member-function-const) +void Child::Ip6AddrEntry::SetMlrState(MlrState aState, Child &aChild) +{ + Ip6AddressArray::IndexType index; -exit: - return; + OT_ASSERT(aChild.mIp6Addresses.IsInArrayBuffer(this)); + + index = aChild.mIp6Addresses.IndexOf(*this); + + aChild.mMlrToRegisterMask.Set(index, aState == kMlrStateToRegister); + aChild.mMlrRegisteredMask.Set(index, aState == kMlrStateRegistered); } +#endif // OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + +//--------------------------------------------------------------------------------------------------------------------- +// Child + void Child::Clear(void) { Instance &instance = GetInstance(); @@ -119,7 +133,7 @@ void Child::Clear(void) void Child::ClearIp6Addresses(void) { mMeshLocalIid.Clear(); - ClearAllBytes(mIp6Address); + mIp6Addresses.Clear(); #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE mMlrToRegisterMask.Clear(); mMlrRegisteredMask.Clear(); @@ -152,6 +166,29 @@ Error Child::GetMeshLocalIp6Address(Ip6::Address &aAddress) const return error; } +Error Child::GetNextIp6Address(AddressIterator &aIterator, Ip6::Address &aAddress) const +{ + Error error = kErrorNone; + + if (aIterator == 0) + { + aIterator++; + + if (GetMeshLocalIp6Address(aAddress) == kErrorNone) + { + ExitNow(); + } + } + + VerifyOrExit(aIterator - 1 < mIp6Addresses.GetLength(), error = kErrorNotFound); + + aAddress = mIp6Addresses[static_cast(aIterator - 1)]; + aIterator++; + +exit: + return error; +} + Error Child::AddIp6Address(const Ip6::Address &aAddress) { Error error = kErrorNone; @@ -165,18 +202,8 @@ Error Child::AddIp6Address(const Ip6::Address &aAddress) ExitNow(); } - for (Ip6::Address &ip6Address : mIp6Address) - { - if (ip6Address.IsUnspecified()) - { - ip6Address = aAddress; - ExitNow(); - } - - VerifyOrExit(ip6Address != aAddress, error = kErrorAlready); - } - - error = kErrorNoBufs; + VerifyOrExit(!mIp6Addresses.ContainsMatching(aAddress), error = kErrorAlready); + error = mIp6Addresses.PushBack(static_cast(aAddress)); exit: return error; @@ -184,10 +211,8 @@ Error Child::AddIp6Address(const Ip6::Address &aAddress) Error Child::RemoveIp6Address(const Ip6::Address &aAddress) { - Error error = kErrorNotFound; - uint16_t index; - - VerifyOrExit(!aAddress.IsUnspecified(), error = kErrorInvalidArgs); + Error error = kErrorNotFound; + Ip6AddrEntry *entry; if (Get().IsMeshLocalAddress(aAddress)) { @@ -200,25 +225,28 @@ Error Child::RemoveIp6Address(const Ip6::Address &aAddress) ExitNow(); } - for (index = 0; index < kNumIp6Addresses; index++) + entry = mIp6Addresses.FindMatching(aAddress); + VerifyOrExit(entry != nullptr); + +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE { - VerifyOrExit(!mIp6Address[index].IsUnspecified()); + // `Array::Remove()` will replace the removed entry with the + // last one in the array. We also update the MLR bit vectors + // to reflect this change. - if (mIp6Address[index] == aAddress) - { - error = kErrorNone; - break; - } - } + uint16_t entryIndex = mIp6Addresses.IndexOf(*entry); + uint16_t lastIndex = mIp6Addresses.GetLength() - 1; - SuccessOrExit(error); + mMlrToRegisterMask.Set(entryIndex, mMlrToRegisterMask.Get(lastIndex)); + mMlrToRegisterMask.Set(lastIndex, false); - for (; index < kNumIp6Addresses - 1; index++) - { - mIp6Address[index] = mIp6Address[index + 1]; + mMlrRegisteredMask.Set(entryIndex, mMlrRegisteredMask.Get(lastIndex)); + mMlrRegisteredMask.Set(lastIndex, false); } +#endif - mIp6Address[kNumIp6Addresses - 1].Clear(); + mIp6Addresses.Remove(*entry); + error = kErrorNone; exit: return error; @@ -226,28 +254,20 @@ Error Child::RemoveIp6Address(const Ip6::Address &aAddress) bool Child::HasIp6Address(const Ip6::Address &aAddress) const { - bool retval = false; + bool hasAddress = false; VerifyOrExit(!aAddress.IsUnspecified()); if (Get().IsMeshLocalAddress(aAddress)) { - retval = (aAddress.GetIid() == mMeshLocalIid); + hasAddress = (aAddress.GetIid() == mMeshLocalIid); ExitNow(); } - for (const Ip6::Address &ip6Address : mIp6Address) - { - VerifyOrExit(!ip6Address.IsUnspecified()); - - if (ip6Address == aAddress) - { - ExitNow(retval = true); - } - } + hasAddress = mIp6Addresses.ContainsMatching(aAddress); exit: - return retval; + return hasAddress; } #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE @@ -255,10 +275,8 @@ Error Child::GetDomainUnicastAddress(Ip6::Address &aAddress) const { Error error = kErrorNotFound; - for (const Ip6::Address &ip6Address : mIp6Address) + for (const Ip6::Address &ip6Address : mIp6Addresses) { - VerifyOrExit(!ip6Address.IsUnspecified()); - if (Get().IsDomainUnicast(ip6Address)) { aAddress = ip6Address; @@ -273,49 +291,21 @@ Error Child::GetDomainUnicastAddress(Ip6::Address &aAddress) const #endif #if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + bool Child::HasMlrRegisteredAddress(const Ip6::Address &aAddress) const { - bool has = false; - - VerifyOrExit(mMlrRegisteredMask.HasAny()); + bool hasAddress = false; + const Ip6AddrEntry *entry; - for (const Ip6::Address &address : IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) - { - if (GetAddressMlrState(address) == kMlrStateRegistered && address == aAddress) - { - ExitNow(has = true); - } - } + entry = mIp6Addresses.FindMatching(aAddress); + VerifyOrExit(entry != nullptr); + hasAddress = entry->GetMlrState(*this) == kMlrStateRegistered; exit: - return has; + return hasAddress; } -MlrState Child::GetAddressMlrState(const Ip6::Address &aAddress) const -{ - uint16_t addressIndex; - - OT_ASSERT(&mIp6Address[0] <= &aAddress && &aAddress < GetArrayEnd(mIp6Address)); - - addressIndex = static_cast(&aAddress - mIp6Address); - - return mMlrToRegisterMask.Get(addressIndex) - ? kMlrStateToRegister - : (mMlrRegisteredMask.Get(addressIndex) ? kMlrStateRegistered : kMlrStateRegistering); -} - -void Child::SetAddressMlrState(const Ip6::Address &aAddress, MlrState aState) -{ - uint16_t addressIndex; - - OT_ASSERT(&mIp6Address[0] <= &aAddress && &aAddress < GetArrayEnd(mIp6Address)); - - addressIndex = static_cast(&aAddress - mIp6Address); - - mMlrToRegisterMask.Set(addressIndex, aState == kMlrStateToRegister); - mMlrRegisteredMask.Set(addressIndex, aState == kMlrStateRegistered); -} -#endif // OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE +#endif #endif // OPENTHREAD_FTD diff --git a/src/core/thread/child.hpp b/src/core/thread/child.hpp index d05fd094a..1069c7393 100644 --- a/src/core/thread/child.hpp +++ b/src/core/thread/child.hpp @@ -42,6 +42,10 @@ namespace ot { #if OPENTHREAD_FTD +#if OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD < 2 +#error OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD should be at least set to 2. +#endif + /** * Represents a Thread Child. * @@ -54,11 +58,27 @@ class Child : public Neighbor, public CslTxScheduler::ChildInfo #endif { - class AddressIteratorBuilder; - public: static constexpr uint8_t kMaxRequestTlvs = 6; + /** + * Maximum number of registered IPv6 addresses per child (excluding the mesh-local EID). + * + */ + static constexpr uint16_t kNumIp6Addresses = OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD - 1; + + /** + * Represents the iterator for registered IPv6 address list of an MTD child. + * + */ + typedef otChildIp6AddressIterator AddressIterator; + + /** + * The initial value for an `AddressIterator`. + * + */ + static constexpr AddressIterator kAddressIteratorInit = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT; + /** * Represents diagnostic information for a Thread Child. * @@ -76,146 +96,53 @@ class Child : public Neighbor, }; /** - * Defines an iterator used to go through IPv6 address entries of a child. + * Represents an IPv6 address entry registered by an MTD child. * */ - class AddressIterator : public Unequatable + class Ip6AddrEntry : public Ip6::Address { - friend class AddressIteratorBuilder; - public: /** - * Represents an index indicating the current IPv6 address entry to which the iterator is pointing. - * - */ - typedef otChildIp6AddressIterator Index; - - /** - * Initializes the iterator associated with a given `Child` starting from beginning of the - * IPv6 address list. - * - * @param[in] aChild A reference to a child entry. - * @param[in] aFilter An IPv6 address type filter restricting iterator to certain type of addresses. - * - */ - explicit AddressIterator(const Child &aChild, Ip6::Address::TypeFilter aFilter = Ip6::Address::kTypeAny) - : AddressIterator(aChild, 0, aFilter) - { - } - - /** - * Initializes the iterator associated with a given `Child` starting from a given index - * - * @param[in] aChild A reference to the child entry. - * @param[in] aIndex An index (`Index`) with which to initialize the iterator. - * @param[in] aFilter An IPv6 address type filter restricting iterator to certain type of addresses. - * - */ - AddressIterator(const Child &aChild, Index aIndex, Ip6::Address::TypeFilter aFilter = Ip6::Address::kTypeAny) - : mChild(aChild) - , mFilter(aFilter) - , mIndex(aIndex) - { - Update(); - } - - /** - * Converts the iterator into an index. - * - * @returns An index corresponding to the iterator. - * - */ - Index GetAsIndex(void) const { return mIndex; } - - /** - * Gets the iterator's associated `Child` entry. - * - * @returns The associated child entry. - * - */ - const Child &GetChild(void) const { return mChild; } - - /** - * Gets the current `Child` IPv6 Address to which the iterator is pointing. - * - * @returns A pointer to the associated IPv6 Address, or `nullptr` if iterator is done. - * - */ - const Ip6::Address *GetAddress(void) const; - - /** - * Indicates whether the iterator has reached end of the list. - * - * @retval TRUE There are no more entries in the list (reached end of the list). - * @retval FALSE The current entry is valid. - * - */ - bool IsDone(void) const { return (mIndex >= kMaxIndex); } - - /** - * Overloads `++` operator (pre-increment) to advance the iterator. + * Indicates whether the entry matches a given IPv6 address. * - * The iterator is moved to point to the next `Address` entry. If there are no more `Ip6::Address` entries - * `IsDone()` returns `true`. + * @param[in] aAddress The IPv6 address. * - */ - void operator++(void) { mIndex++, Update(); } - - /** - * Overloads `++` operator (post-increment) to advance the iterator. - * - * The iterator is moved to point to the next `Address` entry. If there are no more `Ip6::Address` entries - * `IsDone()` returns `true`. + * @retval TRUE The entry matches @p aAddress. + * @retval FALSE The entry does not match @p aAddress. * */ - void operator++(int) { mIndex++, Update(); } + bool Matches(const Ip6::Address &aAddress) const { return (*this == aAddress); } +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE /** - * Overloads the `*` dereference operator and gets a reference to `Ip6::Address` to which the - * iterator is currently pointing. + * Gets the MLR state of the IPv6 address entry. * - * MUST be used when the iterator is not done (i.e., `IsDone()` returns `false`). + * @param[in] aChild The child owning this address entry. * - * @returns A reference to the `Ip6::Address` entry currently pointed by the iterator. + * @returns The MLR state of IPv6 address entry. * */ - const Ip6::Address &operator*(void) const { return *GetAddress(); } + MlrState GetMlrState(const Child &aChild) const; /** - * Overloads operator `==` to evaluate whether or not two `Iterator` instances are equal. - * - * MUST be used when the two iterators are associated with the same `Child` entry. + * Sets the MLR state of the IPv6 address entry. * - * @param[in] aOther The other `Iterator` to compare with. - * - * @retval TRUE If the two `Iterator` objects are equal. - * @retval FALSE If the two `Iterator` objects are not equal. + * @param[in] aState The MLR state. + * @param[in] aChild The child owning this address entry. * */ - bool operator==(const AddressIterator &aOther) const { return (mIndex == aOther.mIndex); } - - private: - enum IteratorType : uint8_t - { - kEndIterator, - }; - - static constexpr uint16_t kMaxIndex = OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD; - - AddressIterator(const Child &aChild, IteratorType) - : mChild(aChild) - , mIndex(kMaxIndex) - { - } - - void Update(void); - - const Child &mChild; - Ip6::Address::TypeFilter mFilter; - Index mIndex; - Ip6::Address mMeshLocalAddress; + void SetMlrState(MlrState aState, Child &aChild); +#endif }; + /** + * Represents an array of IPv6 address entries registered by an MTD child. + * + * This array does not include the mesh-local EID. + * + */ + typedef Array Ip6AddressArray; + /** * Initializes the `Child` object. * @@ -264,26 +191,37 @@ class Child : public Neighbor, const Ip6::InterfaceIdentifier &GetMeshLocalIid(void) const { return mMeshLocalIid; } /** - * Enables range-based `for` loop iteration over all (or a subset of) IPv6 addresses. + * Gets an array of registered IPv6 address entries by the child. * - * Should be used as follows: to iterate over all addresses + * The array does not include the mesh-local EID. The ML-EID can retrieved using `GetMeshLocalIp6Address()`. * - * for (const Ip6::Address &address : child.IterateIp6Addresses()) { ... } + * @returns The array of registered IPv6 addresses by the child. * - * or to iterate over a subset of IPv6 addresses determined by a given address type filter + */ + const Ip6AddressArray &GetIp6Addresses(void) const { return mIp6Addresses; } + + /** + * Gets an array of registered IPv6 address entries by the child. + * + * The array does not include the mesh-local EID. The ML-EID can retrieved using `GetMeshLocalIp6Address()`. * - * for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticast)) { ... } + * @returns The array of registered IPv6 addresses by the child. * - * @param[in] aFilter An IPv6 address type filter restricting iteration to certain type of addresses (default is - * to accept any address type). + */ + Ip6AddressArray &GetIp6Addresses(void) { return mIp6Addresses; } + + /** + * Iterates over all registered IPv6 addresses (using an iterator). * - * @returns An IteratorBuilder instance. + * @param[in,out] aIterator The iterator to use. On success the iterator will be updated. + * To get the first IPv6 address the iterator should be set to `kAddressIteratorInit` + * @param[out] aAddress A reference to an IPv6 address to return the address. + * + * @retval kErrorNone Successfully got the next IPv6 address. @p aIterator and @p aAddress are updated. + * @retval kErrorNotFound No more address. * */ - AddressIteratorBuilder IterateIp6Addresses(Ip6::Address::TypeFilter aFilter = Ip6::Address::kTypeAny) const - { - return AddressIteratorBuilder(*this, aFilter); - } + Error GetNextIp6Address(AddressIterator &aIterator, Ip6::Address &aAddress) const; /** * Adds an IPv6 address to the list. @@ -441,30 +379,7 @@ class Child : public Neighbor, */ void ResetSecondsSinceLastSupervision(void) { mSecondsSinceSupervision = 0; } -#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE - /** - * Returns MLR state of an IPv6 multicast address. - * - * @note The @p aAddress reference MUST be from `IterateIp6Addresses()` or `AddressIterator`. - * - * @param[in] aAddress The IPv6 multicast address. - * - * @returns MLR state of the IPv6 multicast address. - * - */ - MlrState GetAddressMlrState(const Ip6::Address &aAddress) const; - - /** - * Sets MLR state of an IPv6 multicast address. - * - * @note The @p aAddress reference MUST be from `IterateIp6Addresses()` or `AddressIterator`. - * - * @param[in] aAddress The IPv6 multicast address. - * @param[in] aState The target MLR state. - * - */ - void SetAddressMlrState(const Ip6::Address &aAddress, MlrState aState); - +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE /** * Returns if the Child has IPv6 address @p aAddress of MLR state `kMlrStateRegistered`. * @@ -493,53 +408,30 @@ class Child : public Neighbor, * */ bool HasAnyMlrToRegisterAddress(void) const { return mMlrToRegisterMask.HasAny(); } -#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE +#endif // OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE private: -#if OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD < 2 -#error OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD should be at least set to 2. -#endif - - static constexpr uint16_t kNumIp6Addresses = OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD - 1; - typedef BitVector ChildIp6AddressMask; - class AddressIteratorBuilder - { - public: - AddressIteratorBuilder(const Child &aChild, Ip6::Address::TypeFilter aFilter) - : mChild(aChild) - , mFilter(aFilter) - { - } - - AddressIterator begin(void) { return AddressIterator(mChild, mFilter); } - AddressIterator end(void) { return AddressIterator(mChild, AddressIterator::kEndIterator); } - - private: - const Child &mChild; - Ip6::Address::TypeFilter mFilter; - }; - - Ip6::InterfaceIdentifier mMeshLocalIid; ///< IPv6 address IID for mesh-local address - Ip6::Address mIp6Address[kNumIp6Addresses]; ///< Registered IPv6 addresses - uint32_t mTimeout; ///< Child timeout + uint32_t mTimeout; -#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + Ip6::InterfaceIdentifier mMeshLocalIid; + Ip6AddressArray mIp6Addresses; +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE ChildIp6AddressMask mMlrToRegisterMask; ChildIp6AddressMask mMlrRegisteredMask; #endif - uint8_t mNetworkDataVersion; ///< Current Network Data version + uint8_t mNetworkDataVersion; union { - uint8_t mRequestTlvs[kMaxRequestTlvs]; ///< Requested MLE TLVs - Mle::TxChallenge mAttachChallenge; ///< The challenge value + uint8_t mRequestTlvs[kMaxRequestTlvs]; + Mle::TxChallenge mAttachChallenge; }; - uint16_t mSupervisionInterval; // Supervision interval for the child (in sec). - uint16_t mSecondsSinceSupervision; // Number of seconds since last supervision of the child. + uint16_t mSupervisionInterval; + uint16_t mSecondsSinceSupervision; static_assert(OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS < 8192, "mQueuedMessageCount cannot fit max required!"); }; diff --git a/src/core/thread/child_supervision.hpp b/src/core/thread/child_supervision.hpp index ae72f55d1..508442495 100644 --- a/src/core/thread/child_supervision.hpp +++ b/src/core/thread/child_supervision.hpp @@ -102,18 +102,6 @@ class ChildSupervisor : public InstanceLocator, private NonCopyable */ explicit ChildSupervisor(Instance &aInstance); - /** - * Starts the child supervision process on parent. - * - */ - void Start(void); - - /** - * Stops the child supervision process on parent. - * - */ - void Stop(void); - /** * Returns the destination for a supervision message. * diff --git a/src/core/thread/child_table.cpp b/src/core/thread/child_table.cpp index 7fb557dbd..2762bad31 100644 --- a/src/core/thread/child_table.cpp +++ b/src/core/thread/child_table.cpp @@ -192,7 +192,7 @@ Error ChildTable::GetChildInfoById(uint16_t aChildId, Child::Info &aChildInfo) aChildId = Mle::ChildIdFromRloc16(aChildId); } - rloc16 = Get().GetShortAddress() | aChildId; + rloc16 = Get().GetRloc16() | aChildId; child = FindChild(rloc16, Child::kInStateValidOrRestoring); VerifyOrExit(child != nullptr, error = kErrorNotFound); @@ -315,6 +315,22 @@ void ChildTable::RefreshStoredChildren(void) return; } +bool ChildTable::HasMinimalChild(uint16_t aRloc16) const +{ + bool hasMinimalChild = false; + const Child *child; + + VerifyOrExit(Mle::RouterIdMatch(aRloc16, Get().GetRloc16())); + + child = FindChild(Child::AddressMatcher(aRloc16, Child::kInStateValidOrRestoring)); + VerifyOrExit(child != nullptr); + + hasMinimalChild = !child->IsFullThreadDevice(); + +exit: + return hasMinimalChild; +} + bool ChildTable::HasSleepyChildWithAddress(const Ip6::Address &aIp6Address) const { bool hasChild = false; diff --git a/src/core/thread/child_table.hpp b/src/core/thread/child_table.hpp index 61d930742..510db7ef5 100644 --- a/src/core/thread/child_table.hpp +++ b/src/core/thread/child_table.hpp @@ -292,6 +292,19 @@ class ChildTable : public InstanceLocator, private NonCopyable */ Error StoreChild(const Child &aChild); + /** + * Indicates whether or not the child table contains an MTD child with a given @p aRloc16. + * + * Only children in `kInStateValidOrRestoring` are considered. + * + * @param[in] aRloc16 The RLOC16 to check. + * + * @retval TRUE If the child table contains an MTD child with @p aRloc16. + * @retval FALSE If the child table does not contain an MTD child with @p aRloc16. + * + */ + bool HasMinimalChild(uint16_t aRloc16) const; + /** * Indicates whether the child table contains any sleepy child (in states valid or restoring) with a * given IPv6 address. diff --git a/src/core/thread/discover_scanner.cpp b/src/core/thread/discover_scanner.cpp index 9cd997892..03e2ca4eb 100644 --- a/src/core/thread/discover_scanner.cpp +++ b/src/core/thread/discover_scanner.cpp @@ -311,7 +311,6 @@ void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const Error error = kErrorNone; MeshCoP::Tlv meshcopTlv; MeshCoP::DiscoveryResponseTlv discoveryResponse; - MeshCoP::NetworkNameTlv networkName; ScanResult result; uint16_t offset; uint16_t end; @@ -336,12 +335,21 @@ void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const // Process MeshCoP TLVs while (offset < end) { - IgnoreError(aRxInfo.mMessage.Read(offset, meshcopTlv)); + SuccessOrExit(error = aRxInfo.mMessage.Read(offset, meshcopTlv)); + + if (meshcopTlv.IsExtended()) + { + SuccessOrExit(error = Tlv::ParseAndSkipTlv(aRxInfo.mMessage, offset)); + VerifyOrExit(offset <= end, error = kErrorParse); + continue; + } + + VerifyOrExit(meshcopTlv.GetSize() + offset <= aRxInfo.mMessage.GetLength(), error = kErrorParse); switch (meshcopTlv.GetType()) { case MeshCoP::Tlv::kDiscoveryResponse: - IgnoreError(aRxInfo.mMessage.Read(offset, discoveryResponse)); + SuccessOrExit(error = aRxInfo.mMessage.Read(offset, discoveryResponse)); VerifyOrExit(discoveryResponse.IsValid(), error = kErrorParse); result.mVersion = discoveryResponse.GetVersion(); result.mIsNative = discoveryResponse.IsNativeCommissioner(); @@ -353,11 +361,7 @@ void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const break; case MeshCoP::Tlv::kNetworkName: - IgnoreError(aRxInfo.mMessage.Read(offset, networkName)); - if (networkName.IsValid()) - { - IgnoreError(AsCoreType(&result.mNetworkName).Set(networkName.GetNetworkName())); - } + SuccessOrExit(error = Tlv::Read(aRxInfo.mMessage, offset, result.mNetworkName.m8)); break; case MeshCoP::Tlv::kSteeringData: diff --git a/src/core/thread/dua_manager.cpp b/src/core/thread/dua_manager.cpp index 7e3ccc771..808ed9250 100644 --- a/src/core/thread/dua_manager.cpp +++ b/src/core/thread/dua_manager.cpp @@ -472,7 +472,7 @@ void DuaManager::PerformNextRegistration(void) { dua = GetDomainUnicastAddress(); SuccessOrExit(error = Tlv::Append(*message, dua)); - SuccessOrExit(error = Tlv::Append(*message, mle.GetMeshLocal64().GetIid())); + SuccessOrExit(error = Tlv::Append(*message, mle.GetMeshLocalEid().GetIid())); mDuaState = kRegistering; mLastRegistrationTime = TimerMilli::GetNow(); } @@ -512,7 +512,7 @@ void DuaManager::PerformNextRegistration(void) uint8_t pbbrServiceId; SuccessOrExit(error = Get().GetServiceId(pbbrServiceId)); - SuccessOrExit(error = mle.GetServiceAloc(pbbrServiceId, messageInfo.GetPeerAddr())); + mle.GetServiceAloc(pbbrServiceId, messageInfo.GetPeerAddr()); } else { diff --git a/src/core/thread/indirect_sender.cpp b/src/core/thread/indirect_sender.cpp index 89e886917..4fc4d07c5 100644 --- a/src/core/thread/indirect_sender.cpp +++ b/src/core/thread/indirect_sender.cpp @@ -370,7 +370,7 @@ uint16_t IndirectSender::PrepareDataFrame(Mac::TxFrame &aFrame, Child &aChild, M Get().GetMacSourceAddress(ip6Header.GetSource(), macAddrs.mSource); - if (ip6Header.GetDestination().IsLinkLocal()) + if (ip6Header.GetDestination().IsLinkLocalUnicast()) { Get().GetMacDestinationAddress(ip6Header.GetDestination(), macAddrs.mDestination); } diff --git a/src/core/thread/key_manager.cpp b/src/core/thread/key_manager.cpp index d38fdb8b5..9f40c9ec2 100644 --- a/src/core/thread/key_manager.cpp +++ b/src/core/thread/key_manager.cpp @@ -368,11 +368,11 @@ void KeyManager::UpdateKeyMaterial(void) #endif } -void KeyManager::SetCurrentKeySequence(uint32_t aKeySequence, KeySequenceUpdateMode aUpdateMode) +void KeyManager::SetCurrentKeySequence(uint32_t aKeySequence, KeySeqUpdateFlags aFlags) { VerifyOrExit(aKeySequence != mKeySequence, Get().SignalIfFirst(kEventThreadKeySeqCounterChanged)); - if (aUpdateMode == kApplyKeySwitchGuard) + if (aFlags & kApplySwitchGuard) { VerifyOrExit(mKeySwitchGuardTimer == 0); } @@ -384,7 +384,11 @@ void KeyManager::SetCurrentKeySequence(uint32_t aKeySequence, KeySequenceUpdateM mMleFrameCounter = 0; ResetKeyRotationTimer(); - mKeySwitchGuardTimer = mKeySwitchGuardTime; + + if (aFlags & kResetGuardTimer) + { + mKeySwitchGuardTimer = mKeySwitchGuardTime; + } Get().Signal(kEventThreadKeySeqCounterChanged); @@ -528,7 +532,7 @@ void KeyManager::CheckForKeyRotation(void) { if (mHoursSinceKeyRotation >= mSecurityPolicy.mRotationTime) { - SetCurrentKeySequence(mKeySequence + 1, kForceUpdate); + SetCurrentKeySequence(mKeySequence + 1, kForceUpdate | kResetGuardTimer); } } diff --git a/src/core/thread/key_manager.hpp b/src/core/thread/key_manager.hpp index 099854c45..56a64acb3 100644 --- a/src/core/thread/key_manager.hpp +++ b/src/core/thread/key_manager.hpp @@ -221,17 +221,25 @@ class KeyManager : public InstanceLocator, private NonCopyable { public: /** - * Determines whether to apply or ignore key switch guard when updating the key sequence. - * - * Used as input by `SetCurrentKeySequence()`. + * Defines bit-flag constants specifying how to handle key sequence update used in `KeySeqUpdateFlags`. * */ - enum KeySequenceUpdateMode : uint8_t + enum KeySeqUpdateFlag : uint8_t { - kApplyKeySwitchGuard, ///< Apply key switch guard check before setting the new key sequence. - kForceUpdate, ///< Ignore key switch guard check and forcibly update the key sequence to new value. + kApplySwitchGuard = (1 << 0), ///< Apply key switch guard check. + kForceUpdate = (0 << 0), ///< Ignore key switch guard check and forcibly update. + kResetGuardTimer = (1 << 1), ///< On key seq change, reset the guard timer. + kGuardTimerUnchanged = (0 << 1), ///< On key seq change, leave guard timer unchanged. }; + /** + * Represents a combination of `KeySeqUpdateFlag` bits. + * + * Used as input by `SetCurrentKeySequence()`. + * + */ + typedef uint8_t KeySeqUpdateFlags; + /** * Initializes the object. * @@ -342,14 +350,12 @@ class KeyManager : public InstanceLocator, private NonCopyable /** * Sets the current key sequence value. * - * If @p aMode is `kApplyKeySwitchGuard`, the current key switch guard timer is checked and only if it is zero, key - * sequence will be updated. - * * @param[in] aKeySequence The key sequence value. - * @param[in] aUpdateMode Whether or not to apply the key switch guard. + * @param[in] aFlags Specify behavior when updating the key sequence, i.e., whether or not to apply the + * key switch guard or reset guard timer upon change. * */ - void SetCurrentKeySequence(uint32_t aKeySequence, KeySequenceUpdateMode aUpdateMode); + void SetCurrentKeySequence(uint32_t aKeySequence, KeySeqUpdateFlags aFlags); #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE /** diff --git a/src/core/thread/link_metrics.cpp b/src/core/thread/link_metrics.cpp index 9bf6e9a18..840d28e34 100644 --- a/src/core/thread/link_metrics.cpp +++ b/src/core/thread/link_metrics.cpp @@ -146,7 +146,14 @@ void Initiator::HandleReport(const Message &aMessage, uint16_t aOffset, uint16_t { SuccessOrExit(error = aMessage.Read(offset, tlv)); - VerifyOrExit(offset + sizeof(Tlv) + tlv.GetLength() <= endOffset, error = kErrorParse); + if (tlv.IsExtended()) + { + SuccessOrExit(error = Tlv::ParseAndSkipTlv(aMessage, offset)); + VerifyOrExit(offset <= endOffset, error = kErrorParse); + continue; + } + + VerifyOrExit(tlv.GetSize() + offset <= endOffset, error = kErrorParse); // The report must contain either: // - One or more Report Sub-TLVs (in case of success), or @@ -320,6 +327,15 @@ Error Initiator::HandleManagementResponse(const Message &aMessage, const Ip6::Ad SuccessOrExit(error = aMessage.Read(offset, tlv)); + if (tlv.IsExtended()) + { + SuccessOrExit(error = Tlv::ParseAndSkipTlv(aMessage, offset)); + VerifyOrExit(offset <= endOffset, error = kErrorParse); + continue; + } + + VerifyOrExit(tlv.GetSize() + offset <= endOffset, error = kErrorParse); + switch (tlv.GetType()) { case StatusSubTlv::kType: @@ -395,7 +411,7 @@ Error Initiator::FindNeighbor(const Ip6::Address &aDestination, Neighbor *&aNeig aNeighbor = nullptr; - VerifyOrExit(aDestination.IsLinkLocal()); + VerifyOrExit(aDestination.IsLinkLocalUnicast()); aDestination.GetIid().ConvertToMacAddress(macAddress); aNeighbor = Get().FindNeighbor(macAddress); @@ -440,6 +456,15 @@ Error Subject::AppendReport(Message &aMessage, const Message &aRequestMessage, N { SuccessOrExit(error = aRequestMessage.Read(offset, tlv)); + if (tlv.IsExtended()) + { + SuccessOrExit(error = Tlv::ParseAndSkipTlv(aMessage, offset)); + VerifyOrExit(offset <= endOffset, error = kErrorParse); + continue; + } + + VerifyOrExit(tlv.GetSize() + offset <= endOffset, error = kErrorParse); + switch (tlv.GetType()) { case SubTlv::kQueryId: diff --git a/src/core/thread/lowpan.cpp b/src/core/thread/lowpan.cpp index a3d0ef048..3860f7ba0 100644 --- a/src/core/thread/lowpan.cpp +++ b/src/core/thread/lowpan.cpp @@ -345,7 +345,7 @@ Error Lowpan::Compress(Message &aMessage, { hcCtl |= kHcSrcAddrContext; } - else if (ip6Header.GetSource().IsLinkLocal()) + else if (ip6Header.GetSource().IsLinkLocalUnicast()) { SuccessOrExit( error = CompressSourceIid(aMacAddrs.mSource, ip6Header.GetSource(), srcContext, hcCtl, aFrameBuilder)); @@ -366,7 +366,7 @@ Error Lowpan::Compress(Message &aMessage, { SuccessOrExit(error = CompressMulticast(ip6Header.GetDestination(), hcCtl, aFrameBuilder)); } - else if (ip6Header.GetDestination().IsLinkLocal()) + else if (ip6Header.GetDestination().IsLinkLocalUnicast()) { SuccessOrExit(error = CompressDestinationIid(aMacAddrs.mDestination, ip6Header.GetDestination(), dstContext, hcCtl, aFrameBuilder)); diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 4b5b4b20a..14e239b7f 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -628,6 +628,15 @@ Message *MeshForwarder::PrepareNextDirectTransmission(void) switch (error) { case kErrorNone: + if (!curMessage->IsDirectTransmission()) + { + // Skip if message is no longer marked for direct transmission. + // For example, `UpdateIp6Route()` may determine the destination + // is an ALOC associated with an SED child of this device and + // mark it for indirect tx to the SED child. + continue; + } + #if OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE mTxQueueStats.UpdateFor(*curMessage); #endif @@ -670,7 +679,7 @@ Error MeshForwarder::UpdateIp6Route(Message &aMessage) if (mle.IsDisabled() || mle.IsDetached()) { - if (ip6Header.GetDestination().IsLinkLocal() || ip6Header.GetDestination().IsLinkLocalMulticast()) + if (ip6Header.GetDestination().IsLinkLocalUnicastOrMulticast()) { GetMacDestinationAddress(ip6Header.GetDestination(), mMacAddrs.mDestination); } @@ -690,20 +699,20 @@ Error MeshForwarder::UpdateIp6Route(Message &aMessage) if (mle.IsChild() && aMessage.IsLinkSecurityEnabled() && !aMessage.IsSubTypeMle()) { - mMacAddrs.mDestination.SetShort(mle.GetNextHop(Mac::kShortAddrBroadcast)); + mMacAddrs.mDestination.SetShort(mle.GetParentRloc16()); } else { mMacAddrs.mDestination.SetShort(Mac::kShortAddrBroadcast); } } - else if (ip6Header.GetDestination().IsLinkLocal()) + else if (ip6Header.GetDestination().IsLinkLocalUnicast()) { GetMacDestinationAddress(ip6Header.GetDestination(), mMacAddrs.mDestination); } else if (mle.IsMinimalEndDevice()) { - mMacAddrs.mDestination.SetShort(mle.GetNextHop(Mac::kShortAddrBroadcast)); + mMacAddrs.mDestination.SetShort(mle.GetParentRloc16()); } else { @@ -1157,7 +1166,7 @@ void MeshForwarder::UpdateNeighborLinkFailures(Neighbor &aNeighbor, { aNeighbor.IncrementLinkFailures(); - if (aAllowNeighborRemove && (Mle::IsActiveRouter(aNeighbor.GetRloc16())) && + if (aAllowNeighborRemove && (Mle::IsRouterRloc16(aNeighbor.GetRloc16())) && (aNeighbor.GetLinkFailures() >= aFailLimit)) { #if OPENTHREAD_FTD diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index 1ccd0d0ba..eba8e74ba 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -240,15 +240,6 @@ class MeshForwarder : public InstanceLocator, private NonCopyable */ void SetRxOnWhenIdle(bool aRxOnWhenIdle); - /** - * Sets the scan parameters for MLE Discovery Request messages. - * - * @param[in] aScanChannels A reference to channel mask indicating which channels to scan. - * If @p aScanChannels is empty, then all channels are used instead. - * - */ - void SetDiscoverParameters(const Mac::ChannelMask &aScanChannels); - #if OPENTHREAD_FTD /** * Frees any messages queued for an existing child. @@ -501,6 +492,7 @@ class MeshForwarder : public InstanceLocator, private NonCopyable void SendIcmpErrorIfDstUnreach(const Message &aMessage, const Mac::Addresses &aMacAddrs); Error CheckReachability(const FrameData &aFrameData, const Mac::Addresses &aMeshAddrs); + Error CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header); void UpdateRoutes(const FrameData &aFrameData, const Mac::Addresses &aMeshAddrs); Error FrameToMessage(const FrameData &aFrameData, uint16_t aDatagramSize, @@ -510,6 +502,7 @@ class MeshForwarder : public InstanceLocator, private NonCopyable void GetMacSourceAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr); Message *PrepareNextDirectTransmission(void); void HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSource, const ThreadLinkInfo &aLinkInfo); + void ResolveRoutingLoops(uint16_t aSourceRloc16, uint16_t aDestRloc16); void HandleFragment(FrameData &aFrameData, const Mac::Addresses &aMacAddrs, const ThreadLinkInfo &aLinkInfo); void HandleLowpanHC(const FrameData &aFrameData, const Mac::Addresses &aMacAddrs, const ThreadLinkInfo &aLinkInfo); diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index d4911b9a8..966b7f628 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -374,9 +374,9 @@ Error MeshForwarder::UpdateMeshRoute(Message &aMessage) IgnoreError(meshHeader.ParseFrom(aMessage)); - nextHop = Get().GetNextHop(meshHeader.GetDestination()); + nextHop = Get().GetNextHop(meshHeader.GetDestination()); - if (nextHop != Mac::kShortAddrInvalid) + if (nextHop != Mle::kInvalidRloc16) { neighbor = Get().FindNeighbor(nextHop); } @@ -391,7 +391,7 @@ Error MeshForwarder::UpdateMeshRoute(Message &aMessage) } mMacAddrs.mDestination.SetShort(neighbor->GetRloc16()); - mMacAddrs.mSource.SetShort(Get().GetShortAddress()); + mMacAddrs.mSource.SetShort(Get().GetRloc16()); mAddMeshHeader = true; mMeshDest = meshHeader.GetDestination(); @@ -412,7 +412,7 @@ void MeshForwarder::EvaluateRoutingCost(uint16_t aDest, uint8_t &aBestCost, uint { uint8_t cost = Get().GetPathCost(aDest); - if ((aBestDest == Mac::kShortAddrInvalid) || (cost < aBestCost)) + if ((aBestDest == Mle::kInvalidRloc16) || (cost < aBestCost)) { aBestDest = aDest; aBestCost = cost; @@ -423,8 +423,7 @@ Error MeshForwarder::AnycastRouteLookup(uint8_t aServiceId, AnycastType aType, u { NetworkData::Iterator iterator = NetworkData::kIteratorInit; uint8_t bestCost = Mle::kMaxRouteCost; - uint16_t bestDest = Mac::kShortAddrInvalid; - uint8_t routerId; + uint16_t bestDest = Mle::kInvalidRloc16; switch (aType) { @@ -485,19 +484,24 @@ Error MeshForwarder::AnycastRouteLookup(uint8_t aServiceId, AnycastType aType, u } } - routerId = Mle::RouterIdFromRloc16(bestDest); - - if (!(Mle::IsActiveRouter(bestDest) || Mle::Rloc16FromRouterId(routerId) == Get().GetRloc16())) + if (Mle::IsChildRloc16(bestDest)) { - // if agent is neither active router nor child of this device - // use the parent of the ED Agent as Dest - bestDest = Mle::Rloc16FromRouterId(routerId); + // If the selected destination is a child, we use its parent + // as the destination unless the device itself is the + // parent of the `bestDest`. + + uint16_t bestDestParent = Mle::Rloc16FromRouterId(Mle::RouterIdFromRloc16(bestDest)); + + if (Get().GetRloc16() != bestDestParent) + { + bestDest = bestDestParent; + } } aMeshDest = bestDest; exit: - return (bestDest != Mac::kShortAddrInvalid) ? kErrorNone : kErrorNoRoute; + return (bestDest != Mle::kInvalidRloc16) ? kErrorNone : kErrorNoRoute; } Error MeshForwarder::UpdateIp6RouteFtd(const Ip6::Header &aIp6Header, Message &aMessage) @@ -522,7 +526,7 @@ Error MeshForwarder::UpdateIp6RouteFtd(const Ip6::Header &aIp6Header, Message &a if (aloc16 == Mle::kAloc16Leader) { - mMeshDest = Mle::Rloc16FromRouterId(mle.GetLeaderId()); + mMeshDest = mle.GetLeaderRloc16(); } else if (aloc16 <= Mle::kAloc16DhcpAgentEnd) { @@ -556,6 +560,23 @@ Error MeshForwarder::UpdateIp6RouteFtd(const Ip6::Header &aIp6Header, Message &a { ExitNow(error = kErrorDrop); } + + // If the selected ALOC destination, `mMeshDest`, is a sleepy + // child of this device, prepare the message for indirect tx + // to the sleepy child and un-mark message for direct tx. + + if (mle.IsRouterOrLeader() && Mle::IsChildRloc16(mMeshDest) && Mle::RouterIdMatch(mMeshDest, mle.GetRloc16())) + { + Child *child = Get().FindChild(mMeshDest, Child::kInStateValid); + + VerifyOrExit(child != nullptr, error = kErrorDrop); + + if (!child->IsRxOnWhenIdle()) + { + mIndirectSender.AddMessageForSleepyChild(aMessage, *child); + aMessage.ClearDirectTransmission(); + } + } } else if ((neighbor = Get().FindNeighbor(aIp6Header.GetDestination())) != nullptr) { @@ -571,13 +592,13 @@ Error MeshForwarder::UpdateIp6RouteFtd(const Ip6::Header &aIp6Header, Message &a Get().RouteLookup(aIp6Header.GetSource(), aIp6Header.GetDestination(), mMeshDest)); } - VerifyOrExit(mMeshDest != Mac::kShortAddrInvalid, error = kErrorDrop); + VerifyOrExit(mMeshDest != Mle::kInvalidRloc16, error = kErrorDrop); - mMeshSource = Get().GetShortAddress(); + mMeshSource = Get().GetRloc16(); - SuccessOrExit(error = mle.CheckReachability(mMeshDest, aIp6Header)); + SuccessOrExit(error = CheckReachability(mMeshDest, aIp6Header)); aMessage.SetMeshDest(mMeshDest); - mMacAddrs.mDestination.SetShort(mle.GetNextHop(mMeshDest)); + mMacAddrs.mDestination.SetShort(Get().GetNextHop(mMeshDest)); if (mMacAddrs.mDestination.GetShort() != mMeshDest) { @@ -609,7 +630,7 @@ void MeshForwarder::SendIcmpErrorIfDstUnreach(const Message &aMessage, const Mac VerifyOrExit(!ip6Headers.GetDestinationAddress().IsMulticast() && Get().IsOnMesh(ip6Headers.GetDestinationAddress())); - error = Get().CheckReachability(aMacAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header()); + error = CheckReachability(aMacAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header()); if (error == kErrorNoRoute) { @@ -639,7 +660,7 @@ Error MeshForwarder::CheckReachability(const FrameData &aFrameData, const Mac::A ExitNow(); } - error = Get().CheckReachability(aMeshAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header()); + error = CheckReachability(aMeshAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header()); if (error == kErrorNoRoute) { @@ -650,12 +671,49 @@ Error MeshForwarder::CheckReachability(const FrameData &aFrameData, const Mac::A return error; } +Error MeshForwarder::CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header) +{ + bool isReachable = false; + uint16_t deviceRloc16 = Get().GetRloc16(); + + if (Get().IsChild()) + { + if (aMeshDest == deviceRloc16) + { + isReachable = Get().HasUnicastAddress(aIp6Header.GetDestination()); + } + else + { + isReachable = true; + } + + ExitNow(); + } + + if (aMeshDest == deviceRloc16) + { + isReachable = Get().HasUnicastAddress(aIp6Header.GetDestination()) || + (Get().FindNeighbor(aIp6Header.GetDestination()) != nullptr); + ExitNow(); + } + + if (Mle::RouterIdMatch(aMeshDest, deviceRloc16)) + { + isReachable = (Get().FindChild(aMeshDest, Child::kInStateValidOrRestoring) != nullptr); + ExitNow(); + } + + isReachable = (Get().GetNextHop(aMeshDest) != Mle::kInvalidRloc16); + +exit: + return isReachable ? kErrorNone : kErrorNoRoute; +} + void MeshForwarder::SendDestinationUnreachable(uint16_t aMeshSource, const Ip6::Headers &aIp6Headers) { Ip6::MessageInfo messageInfo; - messageInfo.GetPeerAddr() = Get().GetMeshLocal16(); - messageInfo.GetPeerAddr().GetIid().SetLocator(aMeshSource); + messageInfo.GetPeerAddr().SetToRoutingLocator(Get().GetMeshLocalPrefix(), aMeshSource); IgnoreError(Get().SendError(Ip6::Icmp::Header::kTypeDstUnreach, Ip6::Icmp::Header::kCodeDstUnreachNoRoute, messageInfo, aIp6Headers)); @@ -677,8 +735,8 @@ void MeshForwarder::HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSo UpdateRoutes(aFrameData, meshAddrs); - if (meshAddrs.mDestination.GetShort() == Get().GetShortAddress() || - Get().IsMinimalChild(meshAddrs.mDestination.GetShort())) + if (meshAddrs.mDestination.GetShort() == Get().GetRloc16() || + Get().HasMinimalChild(meshAddrs.mDestination.GetShort())) { if (Lowpan::FragmentHeader::IsFragmentHeader(aFrameData)) { @@ -698,7 +756,7 @@ void MeshForwarder::HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSo OwnedPtr messagePtr; Message::Priority priority = Message::kPriorityNormal; - Get().ResolveRoutingLoops(aMacSource.GetShort(), meshAddrs.mDestination.GetShort()); + ResolveRoutingLoops(aMacSource.GetShort(), meshAddrs.mDestination.GetShort()); SuccessOrExit(error = CheckReachability(aFrameData, meshAddrs)); @@ -736,6 +794,27 @@ void MeshForwarder::HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSo } } +void MeshForwarder::ResolveRoutingLoops(uint16_t aSourceRloc16, uint16_t aDestRloc16) +{ + // Resolves 2-hop routing loops. + + Router *router; + + if (aSourceRloc16 != Get().GetNextHop(aDestRloc16)) + { + ExitNow(); + } + + router = Get().FindRouterByRloc16(aDestRloc16); + VerifyOrExit(router != nullptr); + + router->SetNextHopToInvalid(); + Get().ResetAdvertiseInterval(); + +exit: + return; +} + void MeshForwarder::UpdateRoutes(const FrameData &aFrameData, const Mac::Addresses &aMeshAddrs) { Ip6::Headers ip6Headers; @@ -759,7 +838,7 @@ void MeshForwarder::UpdateRoutes(const FrameData &aFrameData, const Mac::Address neighbor = Get().FindNeighbor(ip6Headers.GetSourceAddress()); VerifyOrExit(neighbor != nullptr && !neighbor->IsFullThreadDevice()); - if (!Mle::RouterIdMatch(aMeshAddrs.mSource.GetShort(), Get().GetShortAddress())) + if (!Mle::RouterIdMatch(aMeshAddrs.mSource.GetShort(), Get().GetRloc16())) { Get().RemoveNeighbor(*neighbor); } diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 516a4f9b6..4a009cad9 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -95,8 +95,8 @@ Mle::Mle(Instance &aInstance) #if OPENTHREAD_FTD , mLinkRequestAttempts(0) #endif - , mRloc16(Mac::kShortAddrInvalid) - , mPreviousParentRloc(Mac::kShortAddrInvalid) + , mRloc16(kInvalidRloc16) + , mPreviousParentRloc(kInvalidRloc16) , mAttachCounter(0) , mAnnounceDelay(kAnnounceTimeout) , mAlternatePanId(Mac::kPanIdBroadcast) @@ -106,7 +106,7 @@ Mle::Mle(Instance &aInstance) #endif , mAlternateTimestamp(0) , mNeighborTable(aInstance) - , mSocket(aInstance) + , mSocket(aInstance, *this) #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE , mParentSearch(aInstance) #endif @@ -123,15 +123,15 @@ Mle::Mle(Instance &aInstance) mParentCandidate.Clear(); ResetCounters(); - mLinkLocal64.InitAsThreadOrigin(); - mLinkLocal64.GetAddress().SetToLinkLocalAddress(Get().GetExtAddress()); + mLinkLocalAddress.InitAsThreadOrigin(); + mLinkLocalAddress.GetAddress().SetToLinkLocalAddress(Get().GetExtAddress()); - mMeshLocal64.InitAsThreadOriginMeshLocal(); - mMeshLocal64.GetAddress().GetIid().GenerateRandom(); + mMeshLocalEid.InitAsThreadOriginMeshLocal(); + mMeshLocalEid.GetAddress().GetIid().GenerateRandom(); - mMeshLocal16.InitAsThreadOriginMeshLocal(); - mMeshLocal16.GetAddress().GetIid().SetToLocator(0); - mMeshLocal16.mRloc = true; + mMeshLocalRloc.InitAsThreadOriginMeshLocal(); + mMeshLocalRloc.GetAddress().GetIid().SetToLocator(0); + mMeshLocalRloc.mRloc = true; mLinkLocalAllThreadNodes.Clear(); mLinkLocalAllThreadNodes.GetAddress().mFields.m16[0] = BigEndian::HostSwap16(0xff32); @@ -150,7 +150,7 @@ Error Mle::Enable(void) Error error = kErrorNone; UpdateLinkLocalAddress(); - SuccessOrExit(error = mSocket.Open(&Mle::HandleUdpReceive, this)); + SuccessOrExit(error = mSocket.Open()); SuccessOrExit(error = mSocket.Bind(kUdpPort)); #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE @@ -172,7 +172,7 @@ Error Mle::Disable(void) Stop(kKeepNetworkDatasets); SuccessOrExit(error = mSocket.Close()); - Get().RemoveUnicastAddress(mLinkLocal64); + Get().RemoveUnicastAddress(mLinkLocalAddress); exit: return error; @@ -193,7 +193,7 @@ Error Mle::Start(StartMode aMode) SetStateDetached(); - Get().AddUnicastAddress(mMeshLocal64); + Get().AddUnicastAddress(mMeshLocalEid); Get().SubscribeMulticast(mLinkLocalAllThreadNodes); Get().SubscribeMulticast(mRealmLocalAllThreadNodes); @@ -209,12 +209,12 @@ Error Mle::Start(StartMode aMode) mReattachState = kReattachStart; } - if ((aMode == kAnnounceAttach) || (GetRloc16() == Mac::kShortAddrInvalid)) + if ((aMode == kAnnounceAttach) || (GetRloc16() == kInvalidRloc16)) { Attach(kAnyPartition); } #if OPENTHREAD_FTD - else if (IsActiveRouter(GetRloc16())) + else if (IsRouterRloc16(GetRloc16())) { if (Get().BecomeRouter(ThreadStatusTlv::kTooFewRouters) != kErrorNone) { @@ -246,8 +246,8 @@ void Mle::Stop(StopMode aMode) SetStateDetached(); Get().UnsubscribeMulticast(mRealmLocalAllThreadNodes); Get().UnsubscribeMulticast(mLinkLocalAllThreadNodes); - Get().RemoveUnicastAddress(mMeshLocal16); - Get().RemoveUnicastAddress(mMeshLocal64); + Get().RemoveUnicastAddress(mMeshLocalRloc); + Get().RemoveUnicastAddress(mMeshLocalEid); SetRole(kRoleDisabled); @@ -371,7 +371,8 @@ void Mle::Restore(void) SuccessOrExit(Get().Read(networkInfo)); - Get().SetCurrentKeySequence(networkInfo.GetKeySequence(), KeyManager::kForceUpdate); + Get().SetCurrentKeySequence(networkInfo.GetKeySequence(), + KeyManager::kForceUpdate | KeyManager::kGuardTimerUnchanged); Get().SetMleFrameCounter(networkInfo.GetMleFrameCounter()); Get().SetAllMacFrameCounters(networkInfo.GetMacFrameCounter(), /* aSetIfLarger */ false); @@ -396,7 +397,7 @@ void Mle::Restore(void) } #if OPENTHREAD_MTD - if (!IsActiveRouter(networkInfo.GetRloc16())) + if (IsChildRloc16(networkInfo.GetRloc16())) #endif { Get().SetShortAddress(networkInfo.GetRloc16()); @@ -404,14 +405,14 @@ void Mle::Restore(void) } Get().SetExtAddress(networkInfo.GetExtAddress()); - mMeshLocal64.GetAddress().SetIid(networkInfo.GetMeshLocalIid()); + mMeshLocalEid.GetAddress().SetIid(networkInfo.GetMeshLocalIid()); - if (networkInfo.GetRloc16() == Mac::kShortAddrInvalid) + if (networkInfo.GetRloc16() == kInvalidRloc16) { ExitNow(); } - if (!IsActiveRouter(networkInfo.GetRloc16())) + if (IsChildRloc16(networkInfo.GetRloc16())) { if (Get().Read(parentInfo) != kErrorNone) { @@ -472,7 +473,7 @@ Error Mle::Store(void) networkInfo.SetRloc16(GetRloc16()); networkInfo.SetPreviousPartitionId(mLeaderData.GetPartitionId()); networkInfo.SetExtAddress(Get().GetExtAddress()); - networkInfo.SetMeshLocalIid(mMeshLocal64.GetAddress().GetIid()); + networkInfo.SetMeshLocalIid(mMeshLocalEid.GetAddress().GetIid()); networkInfo.SetVersion(kThreadVersion); if (IsChild()) @@ -539,7 +540,7 @@ Error Mle::BecomeDetached(void) SetStateDetached(); mParent.SetState(Neighbor::kStateInvalid); - SetRloc16(Mac::kShortAddrInvalid); + SetRloc16(kInvalidRloc16); Attach(kAnyPartition); exit: @@ -748,7 +749,7 @@ void Mle::SetStateChild(uint16_t aRloc16) mParentSearch.UpdateState(); #endif - if ((mPreviousParentRloc != Mac::kShortAddrInvalid) && (mPreviousParentRloc != mParent.GetRloc16())) + if ((mPreviousParentRloc != kInvalidRloc16) && (mPreviousParentRloc != mParent.GetRloc16())) { mCounters.mParentChanges++; @@ -879,9 +880,9 @@ Error Mle::SetDeviceMode(DeviceMode aDeviceMode) void Mle::UpdateLinkLocalAddress(void) { - Get().RemoveUnicastAddress(mLinkLocal64); - mLinkLocal64.GetAddress().GetIid().SetFromExtAddress(Get().GetExtAddress()); - Get().AddUnicastAddress(mLinkLocal64); + Get().RemoveUnicastAddress(mLinkLocalAddress); + mLinkLocalAddress.GetAddress().GetIid().SetFromExtAddress(Get().GetExtAddress()); + Get().AddUnicastAddress(mLinkLocalAddress); Get().Signal(kEventThreadLinkLocalAddrChanged); } @@ -908,12 +909,12 @@ void Mle::SetMeshLocalPrefix(const Ip6::NetworkPrefix &aMeshLocalPrefix) // `ApplyNewMeshLocalPrefix()` call, but we apply the new prefix to // them in case they are not yet added to the `Netif`. This ensures // that addresses are always updated and other modules can retrieve - // them using methods such as `GetMeshLocal16()`, `GetMeshLocal64()` + // them using methods such as `GetMeshLocalRloc()`, `GetMeshLocalEid()` // or `GetLinkLocalAllThreadNodesAddress()`, even if they have not // yet been added to the `Netif`. - mMeshLocal64.GetAddress().SetPrefix(mMeshLocalPrefix); - mMeshLocal16.GetAddress().SetPrefix(mMeshLocalPrefix); + mMeshLocalEid.GetAddress().SetPrefix(mMeshLocalPrefix); + mMeshLocalRloc.GetAddress().SetPrefix(mMeshLocalPrefix); mLinkLocalAllThreadNodes.GetAddress().SetMulticastNetworkPrefix(mMeshLocalPrefix); mRealmLocalAllThreadNodes.GetAddress().SetMulticastNetworkPrefix(mMeshLocalPrefix); @@ -932,9 +933,9 @@ Error Mle::SetMeshLocalIid(const Ip6::InterfaceIdentifier &aMlIid) { Error error = kErrorNone; - VerifyOrExit(!Get().HasUnicastAddress(mMeshLocal64), error = kErrorInvalidState); + VerifyOrExit(!Get().HasUnicastAddress(mMeshLocalEid), error = kErrorInvalidState); - mMeshLocal64.GetAddress().SetIid(aMlIid); + mMeshLocalEid.GetAddress().SetIid(aMlIid); exit: return error; } @@ -949,23 +950,23 @@ void Mle::SetRloc16(uint16_t aRloc16) LogNote("RLOC16 %04x -> %04x", oldRloc16, aRloc16); } - if (Get().HasUnicastAddress(mMeshLocal16) && - (mMeshLocal16.GetAddress().GetIid().GetLocator() != aRloc16)) + if (Get().HasUnicastAddress(mMeshLocalRloc) && + (mMeshLocalRloc.GetAddress().GetIid().GetLocator() != aRloc16)) { - Get().RemoveUnicastAddress(mMeshLocal16); - Get().ClearRequests(mMeshLocal16.GetAddress()); + Get().RemoveUnicastAddress(mMeshLocalRloc); + Get().ClearRequests(mMeshLocalRloc.GetAddress()); } Get().SetShortAddress(aRloc16); mRloc16 = aRloc16; - if (aRloc16 != Mac::kShortAddrInvalid) + if (aRloc16 != kInvalidRloc16) { // We can always call `AddUnicastAddress(mMeshLocat16)` and if // the address is already added, it will perform no action. - mMeshLocal16.GetAddress().GetIid().SetLocator(aRloc16); - Get().AddUnicastAddress(mMeshLocal16); + mMeshLocalRloc.GetAddress().GetIid().SetLocator(aRloc16); + Get().AddUnicastAddress(mMeshLocalRloc); #if OPENTHREAD_FTD Get().RestartAddressQueries(); #endif @@ -997,40 +998,21 @@ void Mle::SetLeaderData(uint32_t aPartitionId, uint8_t aWeighting, uint8_t aLead mLeaderData.SetLeaderRouterId(aLeaderRouterId); } -Error Mle::GetLeaderAddress(Ip6::Address &aAddress) const +void Mle::GetLeaderRloc(Ip6::Address &aAddress) const { - Error error = kErrorNone; - - VerifyOrExit(GetRloc16() != Mac::kShortAddrInvalid, error = kErrorDetached); - - aAddress.SetToRoutingLocator(mMeshLocalPrefix, Rloc16FromRouterId(mLeaderData.GetLeaderRouterId())); - -exit: - return error; + aAddress.SetToRoutingLocator(mMeshLocalPrefix, GetLeaderRloc16()); } -Error Mle::GetLocatorAddress(Ip6::Address &aAddress, uint16_t aLocator) const -{ - Error error = kErrorNone; - - VerifyOrExit(GetRloc16() != Mac::kShortAddrInvalid, error = kErrorDetached); - - memcpy(&aAddress, &mMeshLocal16.GetAddress(), 14); - aAddress.GetIid().SetLocator(aLocator); +void Mle::GetLeaderAloc(Ip6::Address &aAddress) const { aAddress.SetToAnycastLocator(mMeshLocalPrefix, kAloc16Leader); } -exit: - return error; +void Mle::GetCommissionerAloc(uint16_t aSessionId, Ip6::Address &aAddress) const +{ + aAddress.SetToAnycastLocator(mMeshLocalPrefix, CommissionerAloc16FromId(aSessionId)); } -Error Mle::GetServiceAloc(uint8_t aServiceId, Ip6::Address &aAddress) const +void Mle::GetServiceAloc(uint8_t aServiceId, Ip6::Address &aAddress) const { - Error error = kErrorNone; - - VerifyOrExit(GetRloc16() != Mac::kShortAddrInvalid, error = kErrorDetached); aAddress.SetToAnycastLocator(mMeshLocalPrefix, ServiceAlocFromId(aServiceId)); - -exit: - return error; } const LeaderData &Mle::GetLeaderData(void) @@ -1050,8 +1032,8 @@ bool Mle::HasUnregisteredAddress(void) for (const Ip6::Netif::UnicastAddress &addr : Get().GetUnicastAddresses()) { - if (!addr.GetAddress().IsLinkLocal() && !IsRoutingLocator(addr.GetAddress()) && - !IsAnycastLocator(addr.GetAddress()) && addr.GetAddress() != GetMeshLocal64()) + if (!addr.GetAddress().IsLinkLocalUnicast() && !IsRoutingLocator(addr.GetAddress()) && + !IsAnycastLocator(addr.GetAddress()) && addr.GetAddress() != GetMeshLocalEid()) { ExitNow(retval = true); } @@ -1117,11 +1099,11 @@ void Mle::HandleNotifierEvents(Events aEvents) if (aEvents.ContainsAny(kEventIp6AddressAdded | kEventIp6AddressRemoved)) { - if (!Get().HasUnicastAddress(mMeshLocal64.GetAddress())) + if (!Get().HasUnicastAddress(mMeshLocalEid.GetAddress())) { - mMeshLocal64.GetAddress().GetIid().GenerateRandom(); + mMeshLocalEid.GetAddress().GetIid().GenerateRandom(); - Get().AddUnicastAddress(mMeshLocal64); + Get().AddUnicastAddress(mMeshLocalEid); Get().Signal(kEventThreadMeshLocalAddrChanged); } @@ -1790,8 +1772,7 @@ Error Mle::SendChildIdRequest(void) VerifyOrExit((message = NewMleMessage(kCommandChildIdRequest)) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->AppendResponseTlv(mParentCandidate.mRxChallenge)); - SuccessOrExit(error = message->AppendLinkFrameCounterTlv()); - SuccessOrExit(error = message->AppendMleFrameCounterTlv()); + SuccessOrExit(error = message->AppendLinkAndMleFrameCounterTlvs()); SuccessOrExit(error = message->AppendModeTlv(mDeviceMode)); SuccessOrExit(error = message->AppendTimeoutTlv(mTimeout)); SuccessOrExit(error = message->AppendVersionTlv()); @@ -2409,11 +2390,6 @@ Error Mle::ProcessMessageSecurity(Crypto::AesCcm::Mode aMode, return error; } -void Mle::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo)); -} - void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error = kErrorNone; @@ -2721,33 +2697,43 @@ void Mle::ProcessKeySequence(RxInfo &aRxInfo) // neighbor. // Otherwise larger key seq MUST NOT be adopted. + bool isNextKeySeq; + KeyManager::KeySeqUpdateFlags flags = 0; + VerifyOrExit(aRxInfo.mKeySequence > Get().GetCurrentKeySequence()); + isNextKeySeq = (aRxInfo.mKeySequence - Get().GetCurrentKeySequence() == 1); + switch (aRxInfo.mClass) { case RxInfo::kAuthoritativeMessage: - Get().SetCurrentKeySequence(aRxInfo.mKeySequence, KeyManager::kForceUpdate); + flags = KeyManager::kForceUpdate; break; case RxInfo::kPeerMessage: - if ((aRxInfo.mNeighbor != nullptr) && aRxInfo.mNeighbor->IsStateValid()) + VerifyOrExit(aRxInfo.IsNeighborStateValid()); + + if (!isNextKeySeq) { - if (aRxInfo.mKeySequence - Get().GetCurrentKeySequence() == 1) - { - Get().SetCurrentKeySequence(aRxInfo.mKeySequence, KeyManager::kApplyKeySwitchGuard); - } - else - { - LogInfo("Large key seq jump in peer class msg from 0x%04x ", aRxInfo.mNeighbor->GetRloc16()); - ReestablishLinkWithNeighbor(*aRxInfo.mNeighbor); - } + LogInfo("Large key seq jump in peer class msg from 0x%04x ", aRxInfo.mNeighbor->GetRloc16()); + ReestablishLinkWithNeighbor(*aRxInfo.mNeighbor); + ExitNow(); } + + flags = KeyManager::kApplySwitchGuard; break; case RxInfo::kUnknown: - break; + ExitNow(); + } + + if (isNextKeySeq) + { + flags |= KeyManager::kResetGuardTimer; } + Get().SetCurrentKeySequence(aRxInfo.mKeySequence, flags); + exit: return; } @@ -2765,7 +2751,7 @@ void Mle::ReestablishLinkWithNeighbor(Neighbor &aNeighbor) #if OPENTHREAD_FTD VerifyOrExit(IsFullThreadDevice()); - if (IsActiveRouter(aNeighbor.GetRloc16())) + if (IsRouterRloc16(aNeighbor.GetRloc16())) { IgnoreError(Get().SendLinkRequest(&aNeighbor)); } @@ -2902,8 +2888,6 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo) bool saveActiveDataset = false; bool savePendingDataset = false; bool dataRequest = false; - uint16_t offset; - uint16_t length; SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)); @@ -2977,16 +2961,15 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo) ExitNow(error = kErrorParse); } - if (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kNetworkData, offset, length) == kErrorNone) - { - error = Get().SetNetworkData(leaderData.GetDataVersion(NetworkData::kFullSet), - leaderData.GetDataVersion(NetworkData::kStableSubset), - GetNetworkDataType(), aRxInfo.mMessage, offset, length); - SuccessOrExit(error); - } - else + switch (error = aRxInfo.mMessage.ReadAndSetNetworkDataTlv(leaderData)) { - ExitNow(dataRequest = true); + case kErrorNone: + break; + case kErrorNotFound: + dataRequest = true; + OT_FALL_THROUGH; + default: + ExitNow(); } #if OPENTHREAD_FTD @@ -2999,19 +2982,16 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo) { // We previously confirmed the message contains an // Active or a Pending Dataset TLV before setting the - // corresponding `saveDataset` flag, so we can safely - // `IgnoreError()` on `FindTlvValueOffset()`. + // corresponding `saveDataset` flag. if (saveActiveDataset) { - IgnoreError(Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kActiveDataset, offset, length)); - IgnoreError(Get().Save(activeTimestamp, aRxInfo.mMessage, offset, length)); + IgnoreError(aRxInfo.mMessage.ReadAndSaveActiveDataset(activeTimestamp)); } if (savePendingDataset) { - IgnoreError(Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kPendingDataset, offset, length)); - IgnoreError(Get().Save(pendingTimestamp, aRxInfo.mMessage, offset, length)); + IgnoreError(aRxInfo.mMessage.ReadAndSavePendingDataset(pendingTimestamp)); } } @@ -3065,7 +3045,7 @@ bool Mle::IsBetterParent(uint16_t aRloc16, rval = ThreeWayCompare(LinkQualityForLinkMargin(aTwoWayLinkMargin), mParentCandidate.GetTwoWayLinkQuality()); VerifyOrExit(rval == 0); - rval = ThreeWayCompare(IsActiveRouter(aRloc16), IsActiveRouter(mParentCandidate.GetRloc16())); + rval = ThreeWayCompare(IsRouterRloc16(aRloc16), IsRouterRloc16(mParentCandidate.GetRloc16())); VerifyOrExit(rval == 0); rval = ThreeWayCompare(aConnectivityTlv.GetParentPriority(), mParentCandidate.mPriority); @@ -3311,10 +3291,6 @@ void Mle::HandleChildIdResponse(RxInfo &aRxInfo) uint16_t sourceAddress; uint16_t shortAddress; MeshCoP::Timestamp timestamp; - uint16_t networkDataOffset; - uint16_t networkDataLength; - uint16_t offset; - uint16_t length; SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, sourceAddress)); @@ -3329,17 +3305,14 @@ void Mle::HandleChildIdResponse(RxInfo &aRxInfo) SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)); - SuccessOrExit( - error = Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kNetworkData, networkDataOffset, networkDataLength)); + VerifyOrExit(aRxInfo.mMessage.ContainsTlv(Tlv::kNetworkData)); switch (Tlv::Find(aRxInfo.mMessage, timestamp)) { case kErrorNone: - if (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kActiveDataset, offset, length) == kErrorNone) - { - SuccessOrExit(error = - Get().Save(timestamp, aRxInfo.mMessage, offset, length)); - } + error = aRxInfo.mMessage.ReadAndSaveActiveDataset(timestamp); + error = (error == kErrorNotFound) ? kErrorNone : error; + SuccessOrExit(error); break; case kErrorNotFound: @@ -3358,10 +3331,7 @@ void Mle::HandleChildIdResponse(RxInfo &aRxInfo) switch (Tlv::Find(aRxInfo.mMessage, timestamp)) { case kErrorNone: - if (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kPendingDataset, offset, length) == kErrorNone) - { - IgnoreError(Get().Save(timestamp, aRxInfo.mMessage, offset, length)); - } + IgnoreError(aRxInfo.mMessage.ReadAndSavePendingDataset(timestamp)); break; case kErrorNotFound: @@ -3398,9 +3368,7 @@ void Mle::HandleChildIdResponse(RxInfo &aRxInfo) mParent.SetRloc16(sourceAddress); - IgnoreError(Get().SetNetworkData( - leaderData.GetDataVersion(NetworkData::kFullSet), leaderData.GetDataVersion(NetworkData::kStableSubset), - GetNetworkDataType(), aRxInfo.mMessage, networkDataOffset, networkDataLength)); + IgnoreError(aRxInfo.mMessage.ReadAndSetNetworkDataTlv(leaderData)); SetStateChild(shortAddress); @@ -3590,7 +3558,7 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo) case kRoleChild: SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, sourceAddress)); - if (RouterIdFromRloc16(sourceAddress) != RouterIdFromRloc16(GetRloc16())) + if (!RouterIdMatch(sourceAddress, GetRloc16())) { IgnoreError(BecomeDetached()); ExitNow(); @@ -3845,11 +3813,7 @@ void Mle::ProcessAnnounce(void) IgnoreError(Start(kAnnounceAttach)); } -uint16_t Mle::GetNextHop(uint16_t aDestination) const -{ - OT_UNUSED_VARIABLE(aDestination); - return (mParent.IsStateValid()) ? mParent.GetRloc16() : static_cast(Mac::kShortAddrInvalid); -} +uint16_t Mle::GetParentRloc16(void) const { return (mParent.IsStateValid() ? mParent.GetRloc16() : kInvalidRloc16); } Error Mle::GetParentInfo(Router::Info &aParentInfo) const { @@ -3891,9 +3855,8 @@ void Mle::InformPreviousParent(void) VerifyOrExit((message = Get().NewMessage(0)) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->SetLength(0)); - messageInfo.SetSockAddr(GetMeshLocal64()); - messageInfo.SetPeerAddr(GetMeshLocal16()); - messageInfo.GetPeerAddr().GetIid().SetLocator(mPreviousParentRloc); + messageInfo.SetSockAddr(GetMeshLocalEid()); + messageInfo.GetPeerAddr().SetToRoutingLocator(mMeshLocalPrefix, mPreviousParentRloc); SuccessOrExit(error = Get().SendDatagram(*message, messageInfo, Ip6::kProtoNone)); @@ -3985,7 +3948,7 @@ void Mle::ParentSearch::UpdateState(void) if (mIsInBackoff && !mBackoffWasCanceled && mRecentlyDetached) { - if ((Get().mPreviousParentRloc != Mac::kShortAddrInvalid) && + if ((Get().mPreviousParentRloc != kInvalidRloc16) && (Get().mPreviousParentRloc != Get().mParent.GetRloc16())) { mIsInBackoff = false; @@ -4009,7 +3972,7 @@ void Mle::ParentSearch::UpdateState(void) #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) void Mle::Log(MessageAction aAction, MessageType aType, const Ip6::Address &aAddress) { - Log(aAction, aType, aAddress, Mac::kShortAddrInvalid); + Log(aAction, aType, aAddress, kInvalidRloc16); } void Mle::Log(MessageAction aAction, MessageType aType, const Ip6::Address &aAddress, uint16_t aRloc) @@ -4021,7 +3984,7 @@ void Mle::Log(MessageAction aAction, MessageType aType, const Ip6::Address &aAdd String rlocString; - if (aRloc != Mac::kShortAddrInvalid) + if (aRloc != kInvalidRloc16) { rlocString.Append(",0x%04x", aRloc); } @@ -4520,6 +4483,17 @@ Error Mle::TxMessage::AppendMleFrameCounterTlv(void) return Tlv::Append(*this, Get().GetMleFrameCounter()); } +Error Mle::TxMessage::AppendLinkAndMleFrameCounterTlvs(void) +{ + Error error; + + SuccessOrExit(error = AppendLinkFrameCounterTlv()); + error = AppendMleFrameCounterTlv(); + +exit: + return error; +} + Error Mle::TxMessage::AppendAddress16Tlv(uint16_t aRloc16) { return Tlv::Append(*this, aRloc16); } Error Mle::TxMessage::AppendLeaderDataTlv(void) @@ -4578,7 +4552,7 @@ Error Mle::TxMessage::AppendAddressRegistrationTlv(AddressRegistrationMode aMode SuccessOrExit(error = Append(tlv)); // Prioritize ML-EID - SuccessOrExit(error = AppendCompressedAddressEntry(kMeshLocalPrefixContextId, Get().GetMeshLocal64())); + SuccessOrExit(error = AppendCompressedAddressEntry(kMeshLocalPrefixContextId, Get().GetMeshLocalEid())); // Continue to append the other addresses if not `kAppendMeshLocalOnly` mode VerifyOrExit(aMode != kAppendMeshLocalOnly); @@ -4597,8 +4571,8 @@ Error Mle::TxMessage::AppendAddressRegistrationTlv(AddressRegistrationMode aMode for (const Ip6::Netif::UnicastAddress &addr : Get().GetUnicastAddresses()) { - if (addr.GetAddress().IsLinkLocal() || Get().IsRoutingLocator(addr.GetAddress()) || - Get().IsAnycastLocator(addr.GetAddress()) || addr.GetAddress() == Get().GetMeshLocal64()) + if (addr.GetAddress().IsLinkLocalUnicast() || Get().IsRoutingLocator(addr.GetAddress()) || + Get().IsAnycastLocator(addr.GetAddress()) || addr.GetAddress() == Get().GetMeshLocalEid()) { continue; } @@ -4800,7 +4774,7 @@ Error Mle::TxMessage::SendTo(const Ip6::Address &aDestination) Ip6::MessageInfo messageInfo; messageInfo.SetPeerAddr(aDestination); - messageInfo.SetSockAddr(Get().mLinkLocal64.GetAddress()); + messageInfo.SetSockAddr(Get().mLinkLocalAddress.GetAddress()); messageInfo.SetPeerPort(kUdpPort); messageInfo.SetHopLimit(kMleHopLimit); @@ -4870,19 +4844,18 @@ Error Mle::TxMessage::AppendAddressRegistrationTlv(Child &aChild) tlv.SetType(Tlv::kAddressRegistration); SuccessOrExit(error = Append(tlv)); - for (const Ip6::Address &address : aChild.IterateIp6Addresses()) + // The parent must echo back all registered IPv6 addresses except + // for the ML-EID, which is excluded by `Child::GetIp6Addresses()`. + + for (const Ip6::Address &address : aChild.GetIp6Addresses()) { if (address.IsMulticast() || Get().GetContext(address, context) != kErrorNone) { SuccessOrExit(error = AppendAddressEntry(address)); } - else if (context.mContextId != kMeshLocalPrefixContextId) - { - SuccessOrExit(error = AppendCompressedAddressEntry(context.mContextId, address)); - } else { - continue; + SuccessOrExit(error = AppendCompressedAddressEntry(context.mContextId, address)); } } @@ -4907,13 +4880,13 @@ Error Mle::TxMessage::AppendActiveDatasetTlv(void) { return AppendDatasetTlv(Mes Error Mle::TxMessage::AppendPendingDatasetTlv(void) { return AppendDatasetTlv(MeshCoP::Dataset::kPending); } -Error Mle::TxMessage::AppendDatasetTlv(MeshCoP::Dataset::Type mDatasetType) +Error Mle::TxMessage::AppendDatasetTlv(MeshCoP::Dataset::Type aDatasetType) { Error error = kErrorNotFound; Tlv::Type tlvType; MeshCoP::Dataset dataset; - switch (mDatasetType) + switch (aDatasetType) { case MeshCoP::Dataset::kActive: error = Get().Read(dataset); @@ -4938,7 +4911,7 @@ Error Mle::TxMessage::AppendDatasetTlv(MeshCoP::Dataset::Type mDatasetType) // message. The Timestamp is appended as its own MLE TLV to the // message. - dataset.RemoveTimestamp(mDatasetType); + dataset.RemoveTimestamp(aDatasetType); error = Tlv::AppendTlv(*this, tlvType, dataset.GetBytes(), dataset.GetLength()); @@ -5074,6 +5047,60 @@ Error Mle::RxMessage::ReadLeaderDataTlv(LeaderData &aLeaderData) const return error; } +Error Mle::RxMessage::ReadAndSetNetworkDataTlv(const LeaderData &aLeaderData) const +{ + Error error; + uint16_t offset; + uint16_t length; + + SuccessOrExit(error = Tlv::FindTlvValueOffset(*this, Tlv::kNetworkData, offset, length)); + + error = Get().SetNetworkData(aLeaderData.GetDataVersion(NetworkData::kFullSet), + aLeaderData.GetDataVersion(NetworkData::kStableSubset), + Get().GetNetworkDataType(), *this, offset, length); +exit: + return error; +} + +Error Mle::RxMessage::ReadAndSaveActiveDataset(const MeshCoP::Timestamp &aActiveTimestamp) const +{ + return ReadAndSaveDataset(MeshCoP::Dataset::kActive, aActiveTimestamp); +} + +Error Mle::RxMessage::ReadAndSavePendingDataset(const MeshCoP::Timestamp &aPendingTimestamp) const +{ + return ReadAndSaveDataset(MeshCoP::Dataset::kPending, aPendingTimestamp); +} + +Error Mle::RxMessage::ReadAndSaveDataset(MeshCoP::Dataset::Type aDatasetType, + const MeshCoP::Timestamp &aTimestamp) const +{ + Error error = kErrorNone; + Tlv::Type tlvType = (aDatasetType == MeshCoP::Dataset::kActive) ? Tlv::kActiveDataset : Tlv::kPendingDataset; + MeshCoP::Dataset dataset; + uint16_t offset; + uint16_t length; + + SuccessOrExit(error = Tlv::FindTlvValueOffset(*this, tlvType, offset, length)); + + SuccessOrExit(error = dataset.SetFrom(*this, offset, length)); + SuccessOrExit(error = dataset.ValidateTlvs()); + SuccessOrExit(error = dataset.WriteTimestamp(aDatasetType, aTimestamp)); + + switch (aDatasetType) + { + case MeshCoP::Dataset::kActive: + error = Get().Save(dataset); + break; + case MeshCoP::Dataset::kPending: + error = Get().Save(dataset); + break; + } + +exit: + return error; +} + Error Mle::RxMessage::ReadTlvRequestTlv(TlvList &aTlvList) const { Error error; diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 7633ffbd2..1dfcfed13 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -390,7 +390,7 @@ class Mle : public InstanceLocator, private NonCopyable * @returns A reference to the Thread link local address. * */ - const Ip6::Address &GetLinkLocalAddress(void) const { return mLinkLocal64.GetAddress(); } + const Ip6::Address &GetLinkLocalAddress(void) const { return mLinkLocalAddress.GetAddress(); } /** * Updates the link local address. @@ -419,6 +419,14 @@ class Mle : public InstanceLocator, private NonCopyable return mRealmLocalAllThreadNodes.GetAddress(); } + /** + * Gets the parent's RLOC16. + * + * @returns The parent's RLOC16, or `kInvalidRloc16` if parent's state is not valid. + * + */ + uint16_t GetParentRloc16(void) const; + /** * Gets the parent when operating in End Device mode. * @@ -516,20 +524,20 @@ class Mle : public InstanceLocator, private NonCopyable uint16_t GetRloc16(void) const { return mRloc16; } /** - * Returns a reference to the RLOC assigned to the Thread interface. + * Returns the mesh local RLOC IPv6 address assigned to the Thread interface. * - * @returns A reference to the RLOC assigned to the Thread interface. + * @returns The mesh local RLOC IPv6 address. * */ - const Ip6::Address &GetMeshLocal16(void) const { return mMeshLocal16.GetAddress(); } + const Ip6::Address &GetMeshLocalRloc(void) const { return mMeshLocalRloc.GetAddress(); } /** - * Returns a reference to the ML-EID assigned to the Thread interface. + * Returns the mesh local endpoint identifier (ML-EID) IPv6 address assigned to the Thread interface. * - * @returns A reference to the ML-EID assigned to the Thread interface. + * @returns The ML-EID address. * */ - const Ip6::Address &GetMeshLocal64(void) const { return mMeshLocal64.GetAddress(); } + const Ip6::Address &GetMeshLocalEid(void) const { return mMeshLocalEid.GetAddress(); } /** * Returns a reference to the ML-EID as a `Netif::UnicastAddress`. @@ -537,7 +545,7 @@ class Mle : public InstanceLocator, private NonCopyable * @returns A reference to the ML-EID. * */ - Ip6::Netif::UnicastAddress &GetMeshLocal64UnicastAddress(void) { return mMeshLocal64; } + Ip6::Netif::UnicastAddress &GetMeshLocalEidUnicastAddress(void) { return mMeshLocalEid; } /** * Returns the Router ID of the Leader. @@ -548,53 +556,46 @@ class Mle : public InstanceLocator, private NonCopyable uint8_t GetLeaderId(void) const { return mLeaderData.GetLeaderRouterId(); } /** - * Retrieves the Leader's RLOC. + * Returns the RLOC16 of the Leader. * - * @param[out] aAddress A reference to the Leader's RLOC. + * @returns The RLOC16 of the Leader. * - * @retval kErrorNone Successfully retrieved the Leader's RLOC. - * @retval kErrorDetached The Thread interface is not currently attached to a Thread Partition. + */ + uint16_t GetLeaderRloc16(void) const { return Rloc16FromRouterId(GetLeaderId()); } + + /** + * Retrieves the Leader's RLOC. + * + * @param[out] aAddress A reference to an address to return the Leader's RLOC. * */ - Error GetLeaderAddress(Ip6::Address &aAddress) const; + void GetLeaderRloc(Ip6::Address &aAddress) const; /** * Retrieves the Leader's ALOC. * - * @param[out] aAddress A reference to the Leader's ALOC. - * - * @retval kErrorNone Successfully retrieved the Leader's ALOC. - * @retval kErrorDetached The Thread interface is not currently attached to a Thread Partition. + * @param[out] aAddress A reference to an address to return the Leader's ALOC. * */ - Error GetLeaderAloc(Ip6::Address &aAddress) const { return GetLocatorAddress(aAddress, kAloc16Leader); } + void GetLeaderAloc(Ip6::Address &aAddress) const; /** - * Computes the Commissioner's ALOC. + * Retrieves the Commissioner's ALOC for a given session ID. * - * @param[out] aAddress A reference to the Commissioner's ALOC. * @param[in] aSessionId Commissioner session id. - * - * @retval kErrorNone Successfully retrieved the Commissioner's ALOC. - * @retval kErrorDetached The Thread interface is not currently attached to a Thread Partition. + * @param[out] aAddress A reference to an address to return the Commissioner's ALOC. * */ - Error GetCommissionerAloc(Ip6::Address &aAddress, uint16_t aSessionId) const - { - return GetLocatorAddress(aAddress, CommissionerAloc16FromId(aSessionId)); - } + void GetCommissionerAloc(uint16_t aSessionId, Ip6::Address &aAddress) const; /** * Retrieves the Service ALOC for given Service ID. * * @param[in] aServiceId Service ID to get ALOC for. - * @param[out] aAddress A reference to the Service ALOC. - * - * @retval kErrorNone Successfully retrieved the Service ALOC. - * @retval kErrorDetached The Thread interface is not currently attached to a Thread Partition. + * @param[out] aAddress A reference to an address to return the Service ALOC. * */ - Error GetServiceAloc(uint8_t aServiceId, Ip6::Address &aAddress) const; + void GetServiceAloc(uint8_t aServiceId, Ip6::Address &aAddress) const; /** * Returns the most recently received Leader Data. @@ -669,18 +670,6 @@ class Mle : public InstanceLocator, private NonCopyable */ void RequestShorterChildIdRequest(void); - /** - * Gets the RLOC or ALOC of a given RLOC16 or ALOC16. - * - * @param[out] aAddress A reference to the RLOC or ALOC. - * @param[in] aLocator RLOC16 or ALOC16. - * - * @retval kErrorNone If got the RLOC or ALOC successfully. - * @retval kErrorDetached If device is detached. - * - */ - Error GetLocatorAddress(Ip6::Address &aAddress, uint16_t aLocator) const; - /** * Schedules a Child Update Request. * @@ -712,16 +701,6 @@ class Mle : public InstanceLocator, private NonCopyable return (&aAddress == &mLinkLocalAllThreadNodes) || (&aAddress == &mRealmLocalAllThreadNodes); } - /** - * Determines the next hop towards an RLOC16 destination. - * - * @param[in] aDestination The RLOC16 of the destination. - * - * @returns A RLOC16 of the next hop if a route is known, kInvalidRloc16 otherwise. - * - */ - uint16_t GetNextHop(uint16_t aDestination) const; - #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE /** * Gets the CSL timeout. @@ -1011,6 +990,7 @@ class Mle : public InstanceLocator, private NonCopyable Error AppendResponseTlv(const RxChallenge &aResponse); Error AppendLinkFrameCounterTlv(void); Error AppendMleFrameCounterTlv(void); + Error AppendLinkAndMleFrameCounterTlvs(void); Error AppendAddress16Tlv(uint16_t aRloc16); Error AppendNetworkDataTlv(NetworkData::Type aType); Error AppendTlvRequestTlv(const uint8_t *aTlvs, uint8_t aTlvsLength); @@ -1055,7 +1035,7 @@ class Mle : public InstanceLocator, private NonCopyable private: Error AppendCompressedAddressEntry(uint8_t aContextId, const Ip6::Address &aAddress); Error AppendAddressEntry(const Ip6::Address &aAddress); - Error AppendDatasetTlv(MeshCoP::Dataset::Type mDatasetType); + Error AppendDatasetTlv(MeshCoP::Dataset::Type aDatasetType); }; //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1072,6 +1052,9 @@ class Mle : public InstanceLocator, private NonCopyable Error ReadFrameCounterTlvs(uint32_t &aLinkFrameCounter, uint32_t &aMleFrameCounter) const; Error ReadTlvRequestTlv(TlvList &aTlvList) const; Error ReadLeaderDataTlv(LeaderData &aLeaderData) const; + Error ReadAndSetNetworkDataTlv(const LeaderData &aLeaderData) const; + Error ReadAndSaveActiveDataset(const MeshCoP::Timestamp &aActiveTimestamp) const; + Error ReadAndSavePendingDataset(const MeshCoP::Timestamp &aPendingTimestamp) const; #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE Error ReadCslClockAccuracyTlv(Mac::CslAccuracy &aCslAccuracy) const; #endif @@ -1081,6 +1064,7 @@ class Mle : public InstanceLocator, private NonCopyable private: Error ReadChallengeOrResponse(uint8_t aTlvType, RxChallenge &aRxChallenge) const; + Error ReadAndSaveDataset(MeshCoP::Dataset::Type aDatasetType, const MeshCoP::Timestamp &aTimestamp) const; }; //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1182,7 +1166,7 @@ class Mle : public InstanceLocator, private NonCopyable class ServiceAloc : public Ip6::Netif::UnicastAddress { public: - static constexpr uint16_t kNotInUse = Mac::kShortAddrInvalid; + static constexpr uint16_t kNotInUse = kInvalidRloc16; ServiceAloc(void); @@ -1236,9 +1220,6 @@ class Mle : public InstanceLocator, private NonCopyable //------------------------------------------------------------------------------------------------------------------ // Methods - static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - static void HandleDetachGracefullyTimer(Timer &aTimer); - Error Start(StartMode aMode); void Stop(StopMode aMode); TxMessage *NewMleMessage(Command aCommand); @@ -1378,6 +1359,7 @@ class Mle : public InstanceLocator, private NonCopyable using AttachTimer = TimerMilliIn; using DelayTimer = TimerMilliIn; using MsgTxTimer = TimerMilliIn; + using MleSocket = Ip6::Udp::SocketIn; static const otMeshLocalPrefix kMeshLocalPrefixInit; @@ -1421,14 +1403,14 @@ class Mle : public InstanceLocator, private NonCopyable uint64_t mLastUpdatedTimestamp; #endif - LeaderData mLeaderData; - Parent mParent; - NeighborTable mNeighborTable; - MessageQueue mDelayedResponses; - TxChallenge mParentRequestChallenge; - ParentCandidate mParentCandidate; - Ip6::Udp::Socket mSocket; - Counters mCounters; + LeaderData mLeaderData; + Parent mParent; + NeighborTable mNeighborTable; + MessageQueue mDelayedResponses; + TxChallenge mParentRequestChallenge; + ParentCandidate mParentCandidate; + MleSocket mSocket; + Counters mCounters; #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE ParentSearch mParentSearch; #endif @@ -1444,9 +1426,9 @@ class Mle : public InstanceLocator, private NonCopyable MsgTxTimer mMessageTransmissionTimer; DetachGracefullyTimer mDetachGracefullyTimer; Ip6::NetworkPrefix mMeshLocalPrefix; - Ip6::Netif::UnicastAddress mLinkLocal64; - Ip6::Netif::UnicastAddress mMeshLocal64; - Ip6::Netif::UnicastAddress mMeshLocal16; + Ip6::Netif::UnicastAddress mLinkLocalAddress; + Ip6::Netif::UnicastAddress mMeshLocalEid; + Ip6::Netif::UnicastAddress mMeshLocalRloc; Ip6::Netif::MulticastAddress mLinkLocalAllThreadNodes; Ip6::Netif::MulticastAddress mRealmLocalAllThreadNodes; }; diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 7c12d8133..e03712345 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -450,7 +450,7 @@ void MleRouter::SetStateRouterOrLeader(DeviceRole aRole, uint16_t aRloc16, Leade if (aRole == kRoleLeader) { - IgnoreError(GetLeaderAloc(mLeaderAloc.GetAddress())); + GetLeaderAloc(mLeaderAloc.GetAddress()); Get().AddUnicastAddress(mLeaderAloc); Get().RegisterReceiver(TimeTicker::kMleRouter); Get().Start(aStartMode); @@ -702,7 +702,7 @@ void MleRouter::HandleLinkRequest(RxInfo &aRxInfo) switch (Tlv::Find(aRxInfo.mMessage, sourceAddress)) { case kErrorNone: - if (IsActiveRouter(sourceAddress)) + if (IsRouterRloc16(sourceAddress)) { neighbor = mRouterTable.FindRouterByRloc16(sourceAddress); VerifyOrExit(neighbor != nullptr, error = kErrorParse); @@ -727,7 +727,7 @@ void MleRouter::HandleLinkRequest(RxInfo &aRxInfo) case kErrorNotFound: // A missing source address indicates that the router was // recently reset. - VerifyOrExit(aRxInfo.IsNeighborStateValid() && IsActiveRouter(aRxInfo.mNeighbor->GetRloc16()), + VerifyOrExit(aRxInfo.IsNeighborStateValid() && IsRouterRloc16(aRxInfo.mNeighbor->GetRloc16()), error = kErrorDrop); neighbor = aRxInfo.mNeighbor; break; @@ -786,13 +786,12 @@ Error MleRouter::SendLinkAccept(const RxInfo &aRxInfo, SuccessOrExit(error = message->AppendVersionTlv()); SuccessOrExit(error = message->AppendSourceAddressTlv()); SuccessOrExit(error = message->AppendResponseTlv(aChallenge)); - SuccessOrExit(error = message->AppendLinkFrameCounterTlv()); - SuccessOrExit(error = message->AppendMleFrameCounterTlv()); + SuccessOrExit(error = message->AppendLinkAndMleFrameCounterTlvs()); linkMargin = Get().ComputeLinkMargin(aRxInfo.mMessage.GetAverageRss()); SuccessOrExit(error = message->AppendLinkMarginTlv(linkMargin)); - if (aNeighbor != nullptr && IsActiveRouter(aNeighbor->GetRloc16())) + if (aNeighbor != nullptr && IsRouterRloc16(aNeighbor->GetRloc16())) { SuccessOrExit(error = message->AppendLeaderDataTlv()); } @@ -892,7 +891,7 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) Log(kMessageReceive, aRequest ? kTypeLinkAcceptAndRequest : kTypeLinkAccept, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress); - VerifyOrExit(IsActiveRouter(sourceAddress), error = kErrorParse); + VerifyOrExit(IsRouterRloc16(sourceAddress), error = kErrorParse); routerId = RouterIdFromRloc16(sourceAddress); router = mRouterTable.FindRouterById(routerId); @@ -959,7 +958,7 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) router = mRouterTable.FindRouterById(routerId); VerifyOrExit(router != nullptr); - if (mLeaderData.GetLeaderRouterId() == RouterIdFromRloc16(GetRloc16())) + if (GetLeaderRloc16() == GetRloc16()) { SetStateLeader(GetRloc16(), kRestoringLeaderRoleAfterReset); } @@ -1085,7 +1084,7 @@ Error MleRouter::ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo) // `RouterTable`). Error error = kErrorNone; - uint16_t neighborRloc16 = Mac::kShortAddrInvalid; + uint16_t neighborRloc16 = kInvalidRloc16; if ((aRxInfo.mNeighbor != nullptr) && Get().Contains(*aRxInfo.mNeighbor)) { @@ -1100,7 +1099,7 @@ Error MleRouter::ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo) error = kErrorNoRoute; } - if (neighborRloc16 != Mac::kShortAddrInvalid) + if (neighborRloc16 != kInvalidRloc16) { aRxInfo.mNeighbor = Get().FindNeighbor(neighborRloc16); } @@ -1250,7 +1249,7 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo, uint16_t aSourceAddress, c ExitNow(); } - VerifyOrExit(IsActiveRouter(aSourceAddress) && routeTlv.IsValid()); + VerifyOrExit(IsRouterRloc16(aSourceAddress) && routeTlv.IsValid()); routerId = RouterIdFromRloc16(aSourceAddress); #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE @@ -1698,8 +1697,7 @@ void MleRouter::SendParentResponse(Child *aChild, const RxChallenge &aChallenge, SuccessOrExit(error = message->AppendSourceAddressTlv()); SuccessOrExit(error = message->AppendLeaderDataTlv()); - SuccessOrExit(error = message->AppendLinkFrameCounterTlv()); - SuccessOrExit(error = message->AppendMleFrameCounterTlv()); + SuccessOrExit(error = message->AppendLinkAndMleFrameCounterTlvs()); SuccessOrExit(error = message->AppendResponseTlv(aChallenge)); #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE if (aChild->IsTimeSyncEnabled()) @@ -1792,12 +1790,16 @@ Error MleRouter::ProcessAddressRegistrationTlv(RxInfo &aRxInfo, Child &aChild) { OT_ASSERT(aChild.IsStateValid()); - for (const Ip6::Address &childAddress : - aChild.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) + for (const Child::Ip6AddrEntry &addrEntry : aChild.GetIp6Addresses()) { - if (aChild.GetAddressMlrState(childAddress) == kMlrStateRegistered) + if (!addrEntry.IsMulticastLargerThanRealmLocal()) { - IgnoreError(oldMlrRegisteredAddresses.PushBack(childAddress)); + continue; + } + + if (addrEntry.GetMlrState(aChild) == kMlrStateRegistered) + { + IgnoreError(oldMlrRegisteredAddresses.PushBack(addrEntry)); } } } @@ -2355,7 +2357,7 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo) LeaderData leaderData; Child *child; - if ((aRxInfo.mNeighbor == nullptr) || IsActiveRouter(aRxInfo.mNeighbor->GetRloc16()) || + if ((aRxInfo.mNeighbor == nullptr) || IsRouterRloc16(aRxInfo.mNeighbor->GetRloc16()) || !Get().Contains(*aRxInfo.mNeighbor)) { Log(kMessageReceive, kTypeChildUpdateResponseOfUnknownChild, aRxInfo.mMessageInfo.GetPeerAddr()); @@ -2635,12 +2637,21 @@ void MleRouter::HandleDiscoveryRequest(RxInfo &aRxInfo) while (offset < end) { - IgnoreError(aRxInfo.mMessage.Read(offset, meshcopTlv)); + SuccessOrExit(error = aRxInfo.mMessage.Read(offset, meshcopTlv)); + + if (meshcopTlv.IsExtended()) + { + SuccessOrExit(error = Tlv::ParseAndSkipTlv(aRxInfo.mMessage, offset)); + VerifyOrExit(offset <= end, error = kErrorParse); + continue; + } + + VerifyOrExit(meshcopTlv.GetSize() + offset <= aRxInfo.mMessage.GetLength(), error = kErrorParse); switch (meshcopTlv.GetType()) { case MeshCoP::Tlv::kDiscoveryRequest: - IgnoreError(aRxInfo.mMessage.Read(offset, discoveryRequestTlv)); + SuccessOrExit(error = aRxInfo.mMessage.Read(offset, discoveryRequestTlv)); VerifyOrExit(discoveryRequestTlv.IsValid(), error = kErrorParse); break; @@ -2698,7 +2709,6 @@ Error MleRouter::SendDiscoveryResponse(const Ip6::Address &aDestination, const M uint16_t startOffset; Tlv tlv; MeshCoP::DiscoveryResponseTlv discoveryResponseTlv; - MeshCoP::NetworkNameTlv networkNameTlv; uint16_t delay; VerifyOrExit((message = NewMleMessage(kCommandDiscoveryResponse)) != nullptr, error = kErrorNoBufs); @@ -2742,9 +2752,8 @@ Error MleRouter::SendDiscoveryResponse(const Ip6::Address &aDestination, const M SuccessOrExit( error = Tlv::Append(*message, Get().GetExtPanId())); - networkNameTlv.Init(); - networkNameTlv.SetNetworkName(Get().GetNetworkName().GetAsData()); - SuccessOrExit(error = networkNameTlv.AppendTo(*message)); + SuccessOrExit(error = Tlv::Append( + *message, Get().GetNetworkName().GetAsCString())); SuccessOrExit(error = message->AppendSteeringDataTlv()); @@ -3109,22 +3118,6 @@ void MleRouter::SendDataResponse(const Ip6::Address &aDestination, LogSendError(kTypeDataResponse, error); } -bool MleRouter::IsMinimalChild(uint16_t aRloc16) -{ - bool rval = false; - - if (RouterIdFromRloc16(aRloc16) == RouterIdFromRloc16(Get().GetShortAddress())) - { - Neighbor *neighbor; - - neighbor = mNeighborTable.FindNeighbor(aRloc16); - - rval = (neighbor != nullptr) && (!neighbor->IsFullThreadDevice()); - } - - return rval; -} - void MleRouter::RemoveRouterLink(Router &aRouter) { switch (mRole) @@ -3161,7 +3154,7 @@ void MleRouter::RemoveNeighbor(Neighbor &aNeighbor) { ClearParentCandidate(); } - else if (!IsActiveRouter(aNeighbor.GetRloc16())) + else if (IsChildRloc16(aNeighbor.GetRloc16())) { OT_ASSERT(mChildTable.Contains(aNeighbor)); @@ -3215,62 +3208,6 @@ void MleRouter::SetRouterId(uint8_t aRouterId) mPreviousRouterId = mRouterId; } -void MleRouter::ResolveRoutingLoops(uint16_t aSourceMac, uint16_t aDestRloc16) -{ - Router *router; - - if (aSourceMac != GetNextHop(aDestRloc16)) - { - ExitNow(); - } - - router = mRouterTable.FindRouterByRloc16(aDestRloc16); - VerifyOrExit(router != nullptr); - - router->SetNextHopToInvalid(); - ResetAdvertiseInterval(); - -exit: - return; -} - -Error MleRouter::CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header) -{ - bool isReachable = false; - - if (IsChild()) - { - if (aMeshDest == GetRloc16()) - { - isReachable = Get().HasUnicastAddress(aIp6Header.GetDestination()); - } - else - { - isReachable = true; - } - - ExitNow(); - } - - if (aMeshDest == GetRloc16()) - { - isReachable = Get().HasUnicastAddress(aIp6Header.GetDestination()) || - (mNeighborTable.FindNeighbor(aIp6Header.GetDestination()) != nullptr); - ExitNow(); - } - - if (RouterIdFromRloc16(aMeshDest) == mRouterId) - { - isReachable = (mChildTable.FindChild(aMeshDest, Child::kInStateValidOrRestoring) != nullptr); - ExitNow(); - } - - isReachable = (GetNextHop(aMeshDest) != Mac::kShortAddrInvalid); - -exit: - return isReachable ? kErrorNone : kErrorNoRoute; -} - Error MleRouter::SendAddressSolicit(ThreadStatusTlv::Status aStatus) { Error error = kErrorNone; @@ -3295,7 +3232,7 @@ Error MleRouter::SendAddressSolicit(ThreadStatusTlv::Status aStatus) SuccessOrExit(error = Tlv::Append(*message, otPlatTimeGetXtalAccuracy())); #endif - SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderRloc()); + messageInfo.SetSockAddrToRlocPeerAddrToLeaderRloc(); SuccessOrExit(error = Get().SendMessage(*message, messageInfo, &HandleAddressSolicitResponse, this)); mAddressSolicitPending = true; @@ -3319,7 +3256,7 @@ void MleRouter::SendAddressRelease(void) SuccessOrExit(error = Tlv::Append(*message, Rloc16FromRouterId(mRouterId))); SuccessOrExit(error = Tlv::Append(*message, Get().GetExtAddress())); - SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderRloc()); + messageInfo.SetSockAddrToRlocPeerAddrToLeaderRloc(); SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); @@ -3489,7 +3426,7 @@ template <> void MleRouter::HandleTmf(Coap::Message &aMessag case kErrorNone: break; case kErrorNotFound: - rloc16 = Mac::kShortAddrInvalid; + rloc16 = kInvalidRloc16; break; default: ExitNow(error = kErrorParse); @@ -3538,7 +3475,7 @@ template <> void MleRouter::HandleTmf(Coap::Message &aMessag ExitNow(); } - if (rloc16 != Mac::kShortAddrInvalid) + if (rloc16 != kInvalidRloc16) { router = mRouterTable.Allocate(RouterIdFromRloc16(rloc16)); diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index e7d7b47c0..3cf3f406b 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -236,16 +236,6 @@ class MleRouter : public Mle */ void SetRouterId(uint8_t aRouterId); - /** - * Returns the next hop towards an RLOC16 destination. - * - * @param[in] aDestination The RLOC16 of the destination. - * - * @returns A RLOC16 of the next hop if a route is known, kInvalidRloc16 otherwise. - * - */ - uint16_t GetNextHop(uint16_t aDestination) { return mRouterTable.GetNextHop(aDestination); } - /** * Returns the NETWORK_ID_TIMEOUT value. * @@ -371,17 +361,6 @@ class MleRouter : public Mle */ void RemoveRouterLink(Router &aRouter); - /** - * Indicates whether or not the RLOC16 is an MTD child of this device. - * - * @param[in] aRloc16 The RLOC16. - * - * @retval TRUE if @p aRloc16 is an MTD child of this device. - * @retval FALSE if @p aRloc16 is not an MTD child of this device. - * - */ - bool IsMinimalChild(uint16_t aRloc16); - /** * Indicates whether or not the given Thread partition attributes are preferred. * @@ -400,27 +379,6 @@ class MleRouter : public Mle bool aSingletonB, const LeaderData &aLeaderDataB); - /** - * Checks if the destination is reachable. - * - * @param[in] aMeshDest The RLOC16 of the destination. - * @param[in] aIp6Header A reference to the IPv6 header of the message. - * - * @retval kErrorNone The destination is reachable. - * @retval kErrorNoRoute The destination is not reachable and the message should be dropped. - * - */ - Error CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header); - - /** - * Resolves 2-hop routing loops. - * - * @param[in] aSourceMac The RLOC16 of the previous hop. - * @param[in] aDestRloc16 The RLOC16 of the final destination. - * - */ - void ResolveRoutingLoops(uint16_t aSourceMac, uint16_t aDestRloc16); - /** * Fills an ConnectivityTlv. * diff --git a/src/core/thread/mle_types.hpp b/src/core/thread/mle_types.hpp index fd08cdccd..a228d06ee 100644 --- a/src/core/thread/mle_types.hpp +++ b/src/core/thread/mle_types.hpp @@ -682,15 +682,26 @@ inline uint16_t CommissionerAloc16FromId(uint16_t aSessionId) inline uint16_t Rloc16FromRouterId(uint8_t aRouterId) { return static_cast(aRouterId << kRouterIdOffset); } /** - * Indicates whether or not @p aRloc16 refers to an active router. + * Indicates whether or not @p aRloc16 refers to a router. * * @param[in] aRloc16 The RLOC16 value. * - * @retval TRUE If @p aRloc16 refers to an active router. - * @retval FALSE If @p aRloc16 does not refer to an active router. + * @retval TRUE If @p aRloc16 refers to a router. + * @retval FALSE If @p aRloc16 does not refer to a router. * */ -inline bool IsActiveRouter(uint16_t aRloc16) { return ChildIdFromRloc16(aRloc16) == 0; } +inline bool IsRouterRloc16(uint16_t aRloc16) { return ChildIdFromRloc16(aRloc16) == 0; } + +/** + * Indicates whether or not @p aRloc16 refers to a child. + * + * @param[in] aRloc16 The RLOC16 value. + * + * @retval TRUE If @p aRloc16 refers to a child. + * @retval FALSE If @p aRloc16 does not refer to a child. + * + */ +inline bool IsChildRloc16(uint16_t aRloc16) { return ChildIdFromRloc16(aRloc16) != 0; } /** * Converts a device role into a human-readable string. diff --git a/src/core/thread/mlr_manager.cpp b/src/core/thread/mlr_manager.cpp index df770fe4f..43f896e70 100644 --- a/src/core/thread/mlr_manager.cpp +++ b/src/core/thread/mlr_manager.cpp @@ -151,18 +151,25 @@ void MlrManager::UpdateProxiedSubscriptions(Child &aChild, const MlrAddressArray VerifyOrExit(aChild.IsStateValid()); // Search the new multicast addresses and set its flag accordingly - for (const Ip6::Address &address : aChild.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) + for (Child::Ip6AddrEntry &addrEntry : aChild.GetIp6Addresses()) { - bool isMlrRegistered = aOldMlrRegisteredAddresses.Contains(address); + bool isMlrRegistered; + + if (!addrEntry.IsMulticastLargerThanRealmLocal()) + { + continue; + } + + isMlrRegistered = aOldMlrRegisteredAddresses.Contains(addrEntry); #if OPENTHREAD_CONFIG_MLR_ENABLE // Check if it's a new multicast address against parent Netif - isMlrRegistered = isMlrRegistered || IsAddressMlrRegisteredByNetif(address); + isMlrRegistered = isMlrRegistered || IsAddressMlrRegisteredByNetif(addrEntry); #endif // Check if it's a new multicast address against other Children - isMlrRegistered = isMlrRegistered || IsAddressMlrRegisteredByAnyChildExcept(address, &aChild); + isMlrRegistered = isMlrRegistered || IsAddressMlrRegisteredByAnyChildExcept(addrEntry, &aChild); - aChild.SetAddressMlrState(address, isMlrRegistered ? kMlrStateRegistered : kMlrStateToRegister); + addrEntry.SetMlrState(isMlrRegistered ? kMlrStateRegistered : kMlrStateToRegister, aChild); } exit: @@ -253,17 +260,22 @@ void MlrManager::SendMlr(void) continue; } - for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) + for (Child::Ip6AddrEntry &addrEntry : child.GetIp6Addresses()) { + if (!addrEntry.IsMulticastLargerThanRealmLocal()) + { + continue; + } + if (addresses.IsFull()) { break; } - if (child.GetAddressMlrState(address) == kMlrStateToRegister) + if (addrEntry.GetMlrState(child) == kMlrStateToRegister) { - addresses.AddUnique(address); - child.SetAddressMlrState(address, kMlrStateRegistering); + addresses.AddUnique(addrEntry); + addrEntry.SetMlrState(kMlrStateRegistering, child); } } } @@ -401,7 +413,7 @@ Error MlrManager::SendMlrMessage(const Ip6::Address *aAddresses, uint8_t pbbrServiceId; SuccessOrExit(error = Get().GetServiceId(pbbrServiceId)); - SuccessOrExit(error = mle.GetServiceAloc(pbbrServiceId, messageInfo.GetPeerAddr())); + mle.GetServiceAloc(pbbrServiceId, messageInfo.GetPeerAddr()); } else { @@ -514,11 +526,16 @@ void MlrManager::SetMulticastAddressMlrState(MlrState aFromState, MlrState aToSt #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE for (Child &child : Get().Iterate(Child::kInStateValid)) { - for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) + for (Child::Ip6AddrEntry &addrEntry : child.GetIp6Addresses()) { - if (child.GetAddressMlrState(address) == aFromState) + if (!addrEntry.IsMulticastLargerThanRealmLocal()) + { + continue; + } + + if (addrEntry.GetMlrState(child) == aFromState) { - child.SetAddressMlrState(address, aToState); + addrEntry.SetMlrState(aToState, child); } } } @@ -546,13 +563,18 @@ void MlrManager::FinishMlr(bool aSuccess, const AddressArray &aFailedAddresses) #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE for (Child &child : Get().Iterate(Child::kInStateValid)) { - for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) + for (Child::Ip6AddrEntry &addrEntry : child.GetIp6Addresses()) { - if (child.GetAddressMlrState(address) == kMlrStateRegistering) + if (!addrEntry.IsMulticastLargerThanRealmLocal()) { - bool success = aSuccess || !aFailedAddresses.IsEmptyOrContains(address); + continue; + } + + if (addrEntry.GetMlrState(child) == kMlrStateRegistering) + { + bool success = aSuccess || !aFailedAddresses.IsEmptyOrContains(addrEntry); - child.SetAddressMlrState(address, success ? kMlrStateRegistered : kMlrStateToRegister); + addrEntry.SetMlrState(success ? kMlrStateRegistered : kMlrStateToRegister, child); } } } @@ -648,11 +670,16 @@ void MlrManager::LogMulticastAddresses(void) #endif #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE - for (Child &child : Get().Iterate(Child::kInStateValid)) + for (const Child &child : Get().Iterate(Child::kInStateValid)) { - for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) + for (const Child::Ip6AddrEntry &addrEntry : child.GetIp6Addresses()) { - LogDebg("%-32s%c %04x", address.ToString().AsCString(), "-rR"[child.GetAddressMlrState(address)], + if (!addrEntry.IsMulticastLargerThanRealmLocal()) + { + continue; + } + + LogDebg("%-32s%c %04x", addrEntry.ToString().AsCString(), "-rR"[addrEntry.GetMlrState(child)], child.GetRloc16()); } } @@ -709,11 +736,16 @@ void MlrManager::CheckInvariants(void) const } #endif #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE - for (Child &child : Get().Iterate(Child::kInStateValid)) + for (const Child &child : Get().Iterate(Child::kInStateValid)) { - for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) + for (const Child::Ip6AddrEntry &addrEntry : child.GetIp6Addresses()) { - registeringNum += (child.GetAddressMlrState(address) == kMlrStateRegistering); + if (!addrEntry.IsMulticastLargerThanRealmLocal()) + { + continue; + } + + registeringNum += (addrEntry.GetMlrState(child) == kMlrStateRegistering); } } #endif diff --git a/src/core/thread/neighbor.hpp b/src/core/thread/neighbor.hpp index 903715d44..b830b4be3 100644 --- a/src/core/thread/neighbor.hpp +++ b/src/core/thread/neighbor.hpp @@ -154,8 +154,7 @@ class Neighbor : public InstanceLocatorInit */ AddressMatcher(const Mac::Address &aMacAddress, StateFilter aStateFilter) : AddressMatcher(aStateFilter, - aMacAddress.IsShort() ? aMacAddress.GetShort() - : static_cast(Mac::kShortAddrInvalid), + aMacAddress.IsShort() ? aMacAddress.GetShort() : Mac::kShortAddrInvalid, aMacAddress.IsExtended() ? &aMacAddress.GetExtended() : nullptr) { } diff --git a/src/core/thread/neighbor_table.cpp b/src/core/thread/neighbor_table.cpp index 217414121..f7a2a7920 100644 --- a/src/core/thread/neighbor_table.cpp +++ b/src/core/thread/neighbor_table.cpp @@ -139,7 +139,7 @@ Neighbor *NeighborTable::FindNeighbor(const Ip6::Address &aIp6Address, Neighbor: Neighbor *neighbor = nullptr; Mac::Address macAddress; - if (aIp6Address.IsLinkLocal()) + if (aIp6Address.IsLinkLocalUnicast()) { aIp6Address.GetIid().ConvertToMacAddress(macAddress); } diff --git a/src/core/thread/network_data.cpp b/src/core/thread/network_data.cpp index 28675ad6f..9ced6a310 100644 --- a/src/core/thread/network_data.cpp +++ b/src/core/thread/network_data.cpp @@ -579,11 +579,11 @@ void NetworkData::AddRloc16ToRlocs(uint16_t aRloc16, Rlocs &aRlocs, RoleFilter a break; case kRouterRoleOnly: - VerifyOrExit(Mle::IsActiveRouter(aRloc16)); + VerifyOrExit(Mle::IsRouterRloc16(aRloc16)); break; case kChildRoleOnly: - VerifyOrExit(!Mle::IsActiveRouter(aRloc16)); + VerifyOrExit(Mle::IsChildRloc16(aRloc16)); break; } diff --git a/src/core/thread/network_data_leader.cpp b/src/core/thread/network_data_leader.cpp index 187e431c2..95b978907 100644 --- a/src/core/thread/network_data_leader.cpp +++ b/src/core/thread/network_data_leader.cpp @@ -339,7 +339,7 @@ int Leader::CompareRouteEntries(int8_t aFirstPreference, // If all the same, prefer the BR acting as a router over an // end device. - result = ThreeWayCompare(Mle::IsActiveRouter(aFirstRloc), Mle::IsActiveRouter(aSecondRloc)); + result = ThreeWayCompare(Mle::IsRouterRloc16(aFirstRloc), Mle::IsRouterRloc16(aSecondRloc)); #endif exit: diff --git a/src/core/thread/network_data_leader_ftd.cpp b/src/core/thread/network_data_leader_ftd.cpp index 9c2914706..4b8b4cb52 100644 --- a/src/core/thread/network_data_leader_ftd.cpp +++ b/src/core/thread/network_data_leader_ftd.cpp @@ -616,7 +616,7 @@ void Leader::CheckForNetDataGettingFull(const NetworkData &aNetworkData, uint16_ leaderClone.MarkAsClone(); SuccessOrAssert(CopyNetworkData(kFullSet, leaderClone)); - if (aOldRloc16 != Mac::kShortAddrInvalid) + if (aOldRloc16 != Mle::kInvalidRloc16) { leaderClone.RemoveBorderRouter(aOldRloc16, kMatchModeRloc16); } diff --git a/src/core/thread/network_data_notifier.cpp b/src/core/thread/network_data_notifier.cpp index e451a73e4..bbcd35bf3 100644 --- a/src/core/thread/network_data_notifier.cpp +++ b/src/core/thread/network_data_notifier.cpp @@ -57,7 +57,7 @@ Notifier::Notifier(Instance &aInstance) , mNetDataFullTask(aInstance) #endif , mNextDelay(0) - , mOldRloc(Mac::kShortAddrInvalid) + , mOldRloc(Mle::kInvalidRloc16) , mWaitingForResponse(false) #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE , mDidRequestRouterRoleUpgrade(false) @@ -138,7 +138,7 @@ Error Notifier::RemoveStaleChildEntries(void) for (uint16_t rloc16 : rlocs) { - if (!Mle::IsActiveRouter(rloc16) && Mle::RouterIdMatch(Get().GetRloc16(), rloc16) && + if (Mle::IsChildRloc16(rloc16) && Mle::RouterIdMatch(Get().GetRloc16(), rloc16) && Get().FindChild(rloc16, Child::kInStateValid) == nullptr) { error = SendServerDataNotification(rloc16); @@ -177,7 +177,7 @@ Error Notifier::UpdateInconsistentData(void) if (mOldRloc == deviceRloc) { - mOldRloc = Mac::kShortAddrInvalid; + mOldRloc = Mle::kInvalidRloc16; } SuccessOrExit(error = SendServerDataNotification(mOldRloc, &Get())); @@ -211,12 +211,12 @@ Error Notifier::SendServerDataNotification(uint16_t aOldRloc16, const NetworkDat #endif } - if (aOldRloc16 != Mac::kShortAddrInvalid) + if (aOldRloc16 != Mle::kInvalidRloc16) { SuccessOrExit(error = Tlv::Append(*message, aOldRloc16)); } - IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); + messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc(); SuccessOrExit(error = Get().SendMessage(*message, messageInfo, HandleCoapResponse, this)); LogInfo("Sent %s", UriToString()); diff --git a/src/core/thread/network_data_publisher.cpp b/src/core/thread/network_data_publisher.cpp index a64f0c39e..5bf16d30e 100644 --- a/src/core/thread/network_data_publisher.cpp +++ b/src/core/thread/network_data_publisher.cpp @@ -275,7 +275,7 @@ bool Publisher::Entry::IsPreferred(uint16_t aRloc16) const // router over an entry from an end-device (e.g., a REED). If both // are the same type, then the one with smaller RLOC16 is preferred. - bool isOtherRouter = Mle::IsActiveRouter(aRloc16); + bool isOtherRouter = Mle::IsRouterRloc16(aRloc16); return (Get().IsRouterOrLeader() == isOtherRouter) ? (aRloc16 < Get().GetRloc16()) : isOtherRouter; @@ -510,7 +510,7 @@ void Publisher::DnsSrpServiceEntry::PublishUnicast(const Ip6::Address &aAddress, void Publisher::DnsSrpServiceEntry::PublishUnicast(uint16_t aPort) { LogInfo("Publishing DNS/SRP service unicast (ml-eid, port:%d)", aPort); - Publish(Info::InfoUnicast(kTypeUnicastMeshLocalEid, Get().GetMeshLocal64(), aPort)); + Publish(Info::InfoUnicast(kTypeUnicastMeshLocalEid, Get().GetMeshLocalEid(), aPort)); } void Publisher::DnsSrpServiceEntry::Publish(const Info &aInfo) @@ -546,7 +546,7 @@ void Publisher::DnsSrpServiceEntry::HandleNotifierEvents(Events aEvents) { if ((GetType() == kTypeUnicastMeshLocalEid) && aEvents.Contains(kEventThreadMeshLocalAddrChanged)) { - mInfo.SetAddress(Get().GetMeshLocal64()); + mInfo.SetAddress(Get().GetMeshLocalEid()); if (GetState() == kAdded) { diff --git a/src/core/thread/network_data_service.cpp b/src/core/thread/network_data_service.cpp index 77ff48597..2891e4c57 100644 --- a/src/core/thread/network_data_service.cpp +++ b/src/core/thread/network_data_service.cpp @@ -103,7 +103,7 @@ void Manager::GetBackboneRouterPrimary(ot::BackboneRouter::Config &aConfig) cons serviceData.Init(&BackboneRouter::kServiceData, BackboneRouter::kServiceDataMinSize); - aConfig.mServer16 = Mac::kShortAddrInvalid; + aConfig.mServer16 = Mle::kInvalidRloc16; while ((serviceTlv = Get().FindNextThreadService(serviceTlv, serviceData, NetworkData::kServicePrefixMatch)) != nullptr) @@ -152,7 +152,7 @@ bool Manager::IsBackboneRouterPreferredTo(const ServerTlv &aSer const BackboneRouter::ServerData &aOtherServerData) const { bool isPreferred; - uint16_t leaderRloc16 = Mle::Rloc16FromRouterId(Get().GetLeaderId()); + uint16_t leaderRloc16 = Get().GetLeaderRloc16(); VerifyOrExit(aServerTlv.GetServer16() != leaderRloc16, isPreferred = true); VerifyOrExit(aOtherServerTlv.GetServer16() != leaderRloc16, isPreferred = false); @@ -182,8 +182,9 @@ Error Manager::GetNextDnsSrpAnycastInfo(Iterator &aIterator, DnsSrpAnycast::Info } while (tlv->GetServiceDataLength() < sizeof(DnsSrpAnycast::ServiceData)); tlv->GetServiceData(serviceData); - aInfo.mAnycastAddress.SetToAnycastLocator(Get().GetMeshLocalPrefix(), - Mle::ServiceAlocFromId(tlv->GetServiceId())); + + Get().GetServiceAloc(tlv->GetServiceId(), aInfo.mAnycastAddress); + aInfo.mSequenceNumber = reinterpret_cast(serviceData.GetBytes())->GetSequenceNumber(); diff --git a/src/core/thread/network_data_tlvs.hpp b/src/core/thread/network_data_tlvs.hpp index 7b15fb275..aefe911d4 100644 --- a/src/core/thread/network_data_tlvs.hpp +++ b/src/core/thread/network_data_tlvs.hpp @@ -43,6 +43,7 @@ #include "common/encoding.hpp" #include "common/equatable.hpp" #include "net/ip6_address.hpp" +#include "thread/mle_types.hpp" #include "thread/network_data_types.hpp" namespace ot { @@ -412,7 +413,7 @@ class HasRouteEntry : public Equatable */ void Init(void) { - SetRloc(Mac::kShortAddrInvalid); + SetRloc(Mle::kInvalidRloc16); mFlags = 0; } @@ -923,7 +924,7 @@ class BorderRouterEntry : public Equatable */ void Init(void) { - SetRloc(Mac::kShortAddrInvalid); + SetRloc(Mle::kInvalidRloc16); mFlags = 0; } diff --git a/src/core/thread/network_diagnostic.cpp b/src/core/thread/network_diagnostic.cpp index f32e7813e..6e168c290 100644 --- a/src/core/thread/network_diagnostic.cpp +++ b/src/core/thread/network_diagnostic.cpp @@ -113,7 +113,7 @@ void Server::PrepareMessageInfoForDest(const Ip6::Address &aDestination, Tmf::Me aMessageInfo.SetMulticastLoop(true); } - if (aDestination.IsLinkLocal() || aDestination.IsLinkLocalMulticast()) + if (aDestination.IsLinkLocalUnicastOrMulticast()) { aMessageInfo.SetSockAddr(Get().GetLinkLocalAddress()); } @@ -750,14 +750,18 @@ Error Server::AppendChildTableIp6AddressList(Coap::Message *&aAnswer, AnswerInfo Error Server::AppendChildIp6AddressListTlv(Coap::Message &aAnswer, const Child &aChild) { Error error = kErrorNone; - uint16_t numIp6Addr = 0; + uint16_t numIp6Addr = aChild.GetIp6Addresses().GetLength(); ChildIp6AddressListTlvValue tlvValue; + Ip6::Address mlEid; - for (const Ip6::Address &address : aChild.IterateIp6Addresses()) + if (aChild.GetMeshLocalIp6Address(mlEid) == kErrorNone) { - OT_UNUSED_VARIABLE(address); numIp6Addr++; } + else + { + mlEid.Clear(); + } VerifyOrExit(numIp6Addr > 0); @@ -782,7 +786,12 @@ Error Server::AppendChildIp6AddressListTlv(Coap::Message &aAnswer, const Child & SuccessOrExit(error = aAnswer.Append(tlvValue)); - for (const Ip6::Address &address : aChild.IterateIp6Addresses()) + if (!mlEid.IsUnspecified()) + { + SuccessOrExit(error = aAnswer.Append(mlEid)); + } + + for (const Ip6::Address &address : aChild.GetIp6Addresses()) { SuccessOrExit(error = aAnswer.Append(address)); } diff --git a/src/core/thread/router_table.cpp b/src/core/thread/router_table.cpp index 1302ce788..16a4d98c5 100644 --- a/src/core/thread/router_table.cpp +++ b/src/core/thread/router_table.cpp @@ -322,7 +322,7 @@ Error RouterTable::GetRouterInfo(uint16_t aRouterId, Router::Info &aRouterInfo) } else { - VerifyOrExit(Mle::IsActiveRouter(aRouterId), error = kErrorInvalidArgs); + VerifyOrExit(Mle::IsRouterRloc16(aRouterId), error = kErrorInvalidArgs); routerId = Mle::RouterIdFromRloc16(aRouterId); VerifyOrExit(routerId <= Mle::kMaxRouterId, error = kErrorInvalidArgs); } @@ -396,10 +396,7 @@ uint8_t RouterTable::GetPathCost(uint16_t aDestRloc16) const return pathCost; } -uint8_t RouterTable::GetPathCostToLeader(void) const -{ - return GetPathCost(Mle::Rloc16FromRouterId(Get().GetLeaderId())); -} +uint8_t RouterTable::GetPathCostToLeader(void) const { return GetPathCost(Get().GetLeaderRloc16()); } void RouterTable::GetNextHopAndPathCost(uint16_t aDestRloc16, uint16_t &aNextHopRloc16, uint8_t &aPathCost) const { @@ -489,7 +486,7 @@ void RouterTable::GetNextHopAndPathCost(uint16_t aDestRloc16, uint16_t &aNextHop } } - if (!Mle::IsActiveRouter(aDestRloc16)) + if (Mle::IsChildRloc16(aDestRloc16)) { // Destination is a child. we assume best link quality // between destination and its parent router. @@ -724,7 +721,7 @@ void RouterTable::FillRouteTlv(Mle::RouteTlv &aRouteTlv, const Neighbor *aNeighb mRouterIdMap.GetAsRouterIdSet(routerIdSet); - if ((aNeighbor != nullptr) && Mle::IsActiveRouter(aNeighbor->GetRloc16())) + if ((aNeighbor != nullptr) && Mle::IsRouterRloc16(aNeighbor->GetRloc16())) { // Sending a Link Accept message that may require truncation // of Route64 TLV. diff --git a/src/core/thread/tmf.cpp b/src/core/thread/tmf.cpp index dec43943d..a0bb80f34 100644 --- a/src/core/thread/tmf.cpp +++ b/src/core/thread/tmf.cpp @@ -42,18 +42,18 @@ namespace Tmf { //---------------------------------------------------------------------------------------------------------------------- // MessageInfo -void MessageInfo::SetSockAddrToRloc(void) { SetSockAddr(Get().GetMeshLocal16()); } +void MessageInfo::SetSockAddrToRloc(void) { SetSockAddr(Get().GetMeshLocalRloc()); } -Error MessageInfo::SetSockAddrToRlocPeerAddrToLeaderAloc(void) +void MessageInfo::SetSockAddrToRlocPeerAddrToLeaderAloc(void) { SetSockAddrToRloc(); - return Get().GetLeaderAloc(GetPeerAddr()); + Get().GetLeaderAloc(GetPeerAddr()); } -Error MessageInfo::SetSockAddrToRlocPeerAddrToLeaderRloc(void) +void MessageInfo::SetSockAddrToRlocPeerAddrToLeaderRloc(void) { SetSockAddrToRloc(); - return Get().GetLeaderAddress(GetPeerAddr()); + Get().GetLeaderRloc(GetPeerAddr()); } void MessageInfo::SetSockAddrToRlocPeerAddrToRealmLocalAllRoutersMulticast(void) @@ -65,8 +65,7 @@ void MessageInfo::SetSockAddrToRlocPeerAddrToRealmLocalAllRoutersMulticast(void) void MessageInfo::SetSockAddrToRlocPeerAddrTo(uint16_t aRloc16) { SetSockAddrToRloc(); - SetPeerAddr(Get().GetMeshLocal16()); - GetPeerAddr().GetIid().SetLocator(aRloc16); + GetPeerAddr().SetToRoutingLocator(Get().GetMeshLocalPrefix(), aRloc16); } void MessageInfo::SetSockAddrToRlocPeerAddrTo(const Ip6::Address &aPeerAddress) @@ -209,9 +208,9 @@ bool Agent::IsTmfMessage(const Ip6::Address &aSourceAddress, const Ip6::Address VerifyOrExit(aDestPort == kUdpPort); - if (aSourceAddress.IsLinkLocal()) + if (aSourceAddress.IsLinkLocalUnicast()) { - isTmf = aDestAddress.IsLinkLocal() || aDestAddress.IsLinkLocalMulticast(); + isTmf = aDestAddress.IsLinkLocalUnicastOrMulticast(); ExitNow(); } diff --git a/src/core/thread/tmf.hpp b/src/core/thread/tmf.hpp index c90e24257..27518705a 100644 --- a/src/core/thread/tmf.hpp +++ b/src/core/thread/tmf.hpp @@ -99,20 +99,14 @@ class MessageInfo : public InstanceLocator, public Ip6::MessageInfo /** * Sets the local socket address to RLOC address and the peer socket address to leader ALOC. * - * @retval kErrorNone Successfully set the addresses. - * @retval kErrorDetached Cannot set leader ALOC since device is currently detached. - * */ - Error SetSockAddrToRlocPeerAddrToLeaderAloc(void); + void SetSockAddrToRlocPeerAddrToLeaderAloc(void); /** * Sets the local socket address to RLOC address and the peer socket address to leader RLOC. - * - * @retval kErrorNone Successfully set the addresses. - * @retval kErrorDetached Cannot set leader RLOC since device is currently detached. - * +q * */ - Error SetSockAddrToRlocPeerAddrToLeaderRloc(void); + void SetSockAddrToRlocPeerAddrToLeaderRloc(void); /** * Sets the local socket address to RLOC address and the peer socket address to realm-local all diff --git a/src/core/utils/history_tracker.hpp b/src/core/utils/history_tracker.hpp index 173292d74..1aa582999 100644 --- a/src/core/utils/history_tracker.hpp +++ b/src/core/utils/history_tracker.hpp @@ -292,7 +292,7 @@ class HistoryTracker : public InstanceLocator, private NonCopyable static constexpr AddressEvent kAddressAdded = OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED; static constexpr AddressEvent kAddressRemoved = OT_HISTORY_TRACKER_ADDRESS_EVENT_REMOVED; - static constexpr uint16_t kInvalidRloc16 = Mac::kShortAddrInvalid; + static constexpr uint16_t kInvalidRloc16 = Mle::kInvalidRloc16; typedef otHistoryTrackerNeighborEvent NeighborEvent; diff --git a/src/core/utils/mesh_diag.cpp b/src/core/utils/mesh_diag.cpp index 07566b6aa..5e470b3f4 100644 --- a/src/core/utils/mesh_diag.cpp +++ b/src/core/utils/mesh_diag.cpp @@ -98,8 +98,7 @@ Error MeshDiag::DiscoverTopology(const DiscoverConfig &aConfig, DiscoverCallback continue; } - destination = Get().GetMeshLocal16(); - destination.GetIid().SetLocator(Mle::Rloc16FromRouterId(routerId)); + destination.SetToRoutingLocator(Get().GetMeshLocalPrefix(), Mle::Rloc16FromRouterId(routerId)); SuccessOrExit(error = Get().SendCommand(kUriDiagnosticGetRequest, Message::kPriorityLow, destination, tlvs, tlvsLength, HandleDiagGetResponse, this)); @@ -173,11 +172,10 @@ Error MeshDiag::SendQuery(uint16_t aRloc16, const uint8_t *aTlvs, uint8_t aTlvsL VerifyOrExit(Get().IsAttached(), error = kErrorInvalidState); VerifyOrExit(mState == kStateIdle, error = kErrorBusy); - VerifyOrExit(Mle::IsActiveRouter(aRloc16), error = kErrorInvalidArgs); + VerifyOrExit(Mle::IsRouterRloc16(aRloc16), error = kErrorInvalidArgs); VerifyOrExit(Get().IsAllocated(Mle::RouterIdFromRloc16(aRloc16)), error = kErrorNotFound); - destination = Get().GetMeshLocal16(); - destination.GetIid().SetLocator(aRloc16); + destination.SetToRoutingLocator(Get().GetMeshLocalPrefix(), aRloc16); SuccessOrExit(error = Get().SendCommand(kUriDiagnosticGetQuery, Message::kPriorityNormal, destination, aTlvs, aTlvsLength)); diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index 6cef38565..605d1deba 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -99,8 +99,8 @@ RadioSpinel::RadioSpinel(void) #endif #if OPENTHREAD_CONFIG_DIAG_ENABLE , mDiagMode(false) - , mDiagOutput(nullptr) - , mDiagOutputMaxLen(0) + , mOutputCallback(nullptr) + , mOutputContext(nullptr) #endif , mTxRadioEndUs(UINT64_MAX) , mRadioTimeRecalcStart(UINT64_MAX) @@ -431,12 +431,13 @@ void RadioSpinel::HandleWaitingResponse(uint32_t aCommand, else if (aKey == SPINEL_PROP_NEST_STREAM_MFG) { spinel_ssize_t unpacked; + const char *diagOutput; mError = OT_ERROR_NONE; - EXPECT(mDiagOutput != nullptr, NO_ACTION); - unpacked = - spinel_datatype_unpack_in_place(aBuffer, aLength, SPINEL_DATATYPE_UTF8_S, mDiagOutput, &mDiagOutputMaxLen); + EXPECT(mOutputCallback != nullptr, NO_ACTION); + unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UTF8_S, &diagOutput); EXPECT(unpacked > 0, mError = OT_ERROR_PARSE); + PlatDiagOutput("%s", diagOutput); } #endif else if (aKey == mWaitingKey) @@ -591,6 +592,17 @@ void RadioSpinel::HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, break; } } +#if OPENTHREAD_CONFIG_DIAG_ENABLE + else if (aKey == SPINEL_PROP_NEST_STREAM_MFG) + { + const char *diagOutput; + + EXPECT(mOutputCallback != nullptr, NO_ACTION); + unpacked = spinel_datatype_unpack(aBuffer, aLength, SPINEL_DATATYPE_UTF8_S, &diagOutput); + EXPECT(unpacked > 0, error = OT_ERROR_PARSE); + PlatDiagOutput("%s", diagOutput); + } +#endif #if OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE else if (aKey >= SPINEL_PROP_VENDOR__BEGIN && aKey < SPINEL_PROP_VENDOR__END) { @@ -1753,20 +1765,31 @@ otError RadioSpinel::Disable(void) } #if OPENTHREAD_CONFIG_DIAG_ENABLE -otError RadioSpinel::PlatDiagProcess(const char *aString, char *aOutput, size_t aOutputMaxLen) +void RadioSpinel::SetDiagOutputCallback(otPlatDiagOutputCallback aCallback, void *aContext) { - otError error; + mOutputCallback = aCallback; + mOutputContext = aContext; +} - mDiagOutput = aOutput; - mDiagOutputMaxLen = aOutputMaxLen; +otError RadioSpinel::PlatDiagProcess(const char *aString) +{ + return Set(SPINEL_PROP_NEST_STREAM_MFG, SPINEL_DATATYPE_UTF8_S, aString); +} - error = Set(SPINEL_PROP_NEST_STREAM_MFG, SPINEL_DATATYPE_UTF8_S, aString); +void RadioSpinel::PlatDiagOutput(const char *aFormat, ...) +{ + va_list args; - mDiagOutput = nullptr; - mDiagOutputMaxLen = 0; + va_start(args, aFormat); - return error; + if (mOutputCallback != nullptr) + { + mOutputCallback(aFormat, args, mOutputContext); + } + + va_end(args); } + #endif uint32_t RadioSpinel::GetRadioChannelMask(bool aPreferred) diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 8fdca562e..4d30ff893 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -34,6 +34,7 @@ #ifndef RADIO_SPINEL_HPP_ #define RADIO_SPINEL_HPP_ +#include #include #include "openthread-spinel-config.h" @@ -678,15 +679,22 @@ class RadioSpinel : private Logger * Processes platform diagnostics commands. * * @param[in] aString A null-terminated input string. - * @param[out] aOutput The diagnostics execution result. - * @param[in] aOutputMaxLen The output buffer size. * * @retval OT_ERROR_NONE Succeeded. * @retval OT_ERROR_BUSY Failed due to another operation is on going. * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. * */ - otError PlatDiagProcess(const char *aString, char *aOutput, size_t aOutputMaxLen); + otError PlatDiagProcess(const char *aString); + + /** + * Sets the diag output callback. + * + * @param[in] aCallback A pointer to a function that is called on outputting diag messages. + * @param[in] aContext A pointer to the user context. + * + */ + void SetDiagOutputCallback(otPlatDiagOutputCallback aCallback, void *aContext); #endif /** @@ -1194,6 +1202,10 @@ class RadioSpinel : private Logger static otError ReadMacKey(const otMacKeyMaterial &aKeyMaterial, otMacKey &aKey); #endif +#if OPENTHREAD_CONFIG_DIAG_ENABLE + void PlatDiagOutput(const char *aFormat, ...); +#endif + otInstance *mInstance; RadioSpinelCallbacks mCallbacks; ///< Callbacks for notifications of higher layer. @@ -1281,9 +1293,9 @@ class RadioSpinel : private Logger #endif // OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 #if OPENTHREAD_CONFIG_DIAG_ENABLE - bool mDiagMode; - char *mDiagOutput; - size_t mDiagOutputMaxLen; + bool mDiagMode; + otPlatDiagOutputCallback mOutputCallback; + void *mOutputContext; #endif uint64_t mTxRadioEndUs; diff --git a/src/lib/spinel/spinel.c b/src/lib/spinel/spinel.c index 683e58fdf..03c107c5a 100644 --- a/src/lib/spinel/spinel.c +++ b/src/lib/spinel/spinel.c @@ -1287,6 +1287,7 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) {SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING, "NET_REQUIRE_JOIN_EXISTING"}, {SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME, "NET_KEY_SWITCH_GUARDTIME"}, {SPINEL_PROP_NET_PSKC, "NET_PSKC"}, + {SPINEL_PROP_NET_LEAVE_GRACEFULLY, "NET_LEAVE_GRACEFULLY"}, {SPINEL_PROP_THREAD_LEADER_ADDR, "THREAD_LEADER_ADDR"}, {SPINEL_PROP_THREAD_PARENT, "THREAD_PARENT"}, {SPINEL_PROP_THREAD_CHILD_TABLE, "THREAD_CHILD_TABLE"}, diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index 4d5fc28d9..9941dba75 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -2370,6 +2370,12 @@ enum */ SPINEL_PROP_NET_PSKC = SPINEL_PROP_NET__BEGIN + 11, + /// Instruct NCP to leave the current network gracefully + /** Format Empty - Write only + * + */ + SPINEL_PROP_NET_LEAVE_GRACEFULLY = SPINEL_PROP_NET__BEGIN + 12, + SPINEL_PROP_NET__END = 0x50, SPINEL_PROP_NET_EXT__BEGIN = 0x1400, diff --git a/src/lib/spinel/spinel_driver.cpp b/src/lib/spinel/spinel_driver.cpp index e802b29b4..421ac47ed 100644 --- a/src/lib/spinel/spinel_driver.cpp +++ b/src/lib/spinel/spinel_driver.cpp @@ -192,6 +192,7 @@ otError SpinelDriver::SendCommand(uint32_t aCommand, spinel_prop_key_t aKey, spi offset = static_cast(packed); EXPECT_NO_ERROR(error = mSpinelInterface->SendFrame(buffer, offset)); + LogSpinelFrame(buffer, offset, true /* aTx */); exit: return error; @@ -226,6 +227,7 @@ otError SpinelDriver::SendCommand(uint32_t aCommand, } EXPECT_NO_ERROR(error = mSpinelInterface->SendFrame(buffer, offset)); + LogSpinelFrame(buffer, offset, true /* aTx */); exit: return error; diff --git a/src/lib/spinel/spinel_driver.hpp b/src/lib/spinel/spinel_driver.hpp index 1f45bc719..5a3090c83 100644 --- a/src/lib/spinel/spinel_driver.hpp +++ b/src/lib/spinel/spinel_driver.hpp @@ -36,6 +36,14 @@ #include "lib/spinel/spinel.h" #include "lib/spinel/spinel_interface.hpp" +/** + * Represents an opaque (and empty) type corresponding to a SpinelDriver object. + * + */ +struct otSpinelDriver +{ +}; + namespace ot { namespace Spinel { @@ -49,7 +57,7 @@ static constexpr uint8_t kSpinelHeaderMaxNumIid = 4; static constexpr uint8_t kSpinelHeaderMaxNumIid = 1; #endif -class SpinelDriver : public Logger +class SpinelDriver : public otSpinelDriver, public Logger { public: typedef void ( diff --git a/src/ncp/changed_props_set.cpp b/src/ncp/changed_props_set.cpp index 9e847f05c..f81bbf0d1 100644 --- a/src/ncp/changed_props_set.cpp +++ b/src/ncp/changed_props_set.cpp @@ -77,6 +77,7 @@ const ChangedPropsSet::Entry ChangedPropsSet::mSupportedProps[] = { {SPINEL_PROP_NET_XPANID, SPINEL_STATUS_OK, true}, {SPINEL_PROP_NET_NETWORK_KEY, SPINEL_STATUS_OK, true}, {SPINEL_PROP_NET_PSKC, SPINEL_STATUS_OK, true}, + {SPINEL_PROP_NET_LEAVE_GRACEFULLY, SPINEL_STATUS_OK, false}, {SPINEL_PROP_PHY_CHAN_SUPPORTED, SPINEL_STATUS_OK, true}, #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE {SPINEL_PROP_CHANNEL_MANAGER_NEW_CHANNEL, SPINEL_STATUS_OK, true}, diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index 6b99b4d4a..fc6b56529 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -313,6 +313,10 @@ NcpBase::NcpBase(Instance *aInstance) , mTxSpinelFrameCounter(0) , mDidInitialUpdates(false) , mLogTimestampBase(0) +#if OPENTHREAD_CONFIG_DIAG_ENABLE + , mDiagOutput(nullptr) + , mDiagOutputLen(0) +#endif { OT_ASSERT(mInstance != nullptr); @@ -354,6 +358,9 @@ NcpBase::NcpBase(Instance *aInstance) otSrpClientSetCallback(mInstance, HandleSrpClientCallback, this); #endif #endif // OPENTHREAD_MTD || OPENTHREAD_FTD +#if OPENTHREAD_CONFIG_DIAG_ENABLE + otDiagSetOutputCallback(mInstance, &NcpBase::HandleDiagOutput_Jump, this); +#endif mChangedPropsSet.AddLastStatus(SPINEL_STATUS_RESET_UNKNOWN); mUpdateChangedPropsTask.Post(); @@ -1418,12 +1425,11 @@ otError NcpBase::CommandHandler_POKE(uint8_t aHeader) // ---------------------------------------------------------------------------- #if OPENTHREAD_CONFIG_DIAG_ENABLE - otError NcpBase::HandlePropertySet_SPINEL_PROP_NEST_STREAM_MFG(uint8_t aHeader) { - const char *string = nullptr; - char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; - otError error = OT_ERROR_NONE; + const char *string = nullptr; + char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE] = {0}; + otError error = OT_ERROR_NONE; error = mDecoder.ReadUtf8(string); @@ -1438,7 +1444,10 @@ otError NcpBase::HandlePropertySet_SPINEL_PROP_NEST_STREAM_MFG(uint8_t aHeader) } #endif - SuccessOrExit(error = otDiagProcessCmdLine(mInstance, string, output, sizeof(output))); + mDiagOutput = output; + mDiagOutputLen = sizeof(output); + + SuccessOrExit(error = otDiagProcessCmdLine(mInstance, string)); // Prepare the response SuccessOrExit(error = mEncoder.BeginFrame(aHeader, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_NEST_STREAM_MFG)); @@ -1446,9 +1455,46 @@ otError NcpBase::HandlePropertySet_SPINEL_PROP_NEST_STREAM_MFG(uint8_t aHeader) SuccessOrExit(error = mEncoder.EndFrame()); exit: + mDiagOutput = nullptr; + mDiagOutputLen = 0; + return error; } +void NcpBase::HandleDiagOutput_Jump(const char *aFormat, va_list aArguments, void *aContext) +{ + static_cast(aContext)->HandleDiagOutput(aFormat, aArguments); +} + +void NcpBase::HandleDiagOutput(const char *aFormat, va_list aArguments) +{ + int charsWritten; + + if (mDiagOutput != nullptr) + { + charsWritten = vsnprintf(mDiagOutput, mDiagOutputLen, aFormat, aArguments); + VerifyOrExit(charsWritten > 0); + charsWritten = (mDiagOutputLen <= charsWritten) ? mDiagOutputLen : charsWritten; + mDiagOutput += charsWritten; + mDiagOutputLen -= charsWritten; + } + else + { + uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; + char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; + + charsWritten = vsnprintf(output, sizeof(output), aFormat, aArguments); + VerifyOrExit(charsWritten >= 0); + + SuccessOrExit(mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_NEST_STREAM_MFG)); + SuccessOrExit(mEncoder.WriteUtf8(output)); + SuccessOrExit(mEncoder.EndFrame()); + } + +exit: + return; +} + #endif // OPENTHREAD_CONFIG_DIAG_ENABLE template <> otError NcpBase::HandlePropertyGet(void) diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index 7b8d94f87..831705791 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -549,6 +549,11 @@ class NcpBase static uint8_t ConvertLogLevel(otLogLevel aLogLevel); static unsigned int ConvertLogRegion(otLogRegion aLogRegion); +#if OPENTHREAD_CONFIG_DIAG_ENABLE + static void HandleDiagOutput_Jump(const char *aFormat, va_list aArguments, void *aContext); + void HandleDiagOutput(const char *aFormat, va_list aArguments); +#endif + #if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK /** * Defines a vendor "command handler" hook to process vendor-specific spinel commands. @@ -615,6 +620,10 @@ class NcpBase #endif // OPENTHREAD_ENABLE_NCP_VENDOR_HOOK + static void ThreadDetachGracefullyHandler(void *aContext); + + void ThreadDetachGracefullyHandler(void); + protected: static NcpBase *sNcpInstance; static spinel_status_t ThreadErrorToSpinelStatus(otError aError); @@ -730,6 +739,11 @@ class NcpBase bool mDidInitialUpdates; uint64_t mLogTimestampBase; // Timestamp base used for logging + +#if OPENTHREAD_CONFIG_DIAG_ENABLE + char *mDiagOutput; + uint16_t mDiagOutputLen; +#endif }; } // namespace Ncp diff --git a/src/ncp/ncp_base_dispatcher.cpp b/src/ncp/ncp_base_dispatcher.cpp index 112f5022f..167b7348a 100644 --- a/src/ncp/ncp_base_dispatcher.cpp +++ b/src/ncp/ncp_base_dispatcher.cpp @@ -100,6 +100,7 @@ NcpBase::PropertyHandler NcpBase::FindGetPropertyHandler(spinel_prop_key_t aKey) #if OPENTHREAD_FTD OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_NET_PSKC), #endif + OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_NET_LEAVE_GRACEFULLY), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_THREAD_LEADER_ADDR), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_THREAD_PARENT), #if OPENTHREAD_FTD @@ -452,6 +453,9 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME), #if OPENTHREAD_FTD OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_NET_PSKC), +#endif + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_NET_LEAVE_GRACEFULLY), +#if OPENTHREAD_FTD OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_THREAD_LOCAL_LEADER_WEIGHT), #endif OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_THREAD_ASSISTING_PORTS), diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index 227ce61ce..07047217f 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -4552,6 +4552,24 @@ template <> otError NcpBase::HandlePropertySet(voi return error; } +template <> otError NcpBase::HandlePropertyGet(void) { return OT_ERROR_NONE; } + +template <> otError NcpBase::HandlePropertySet(void) +{ + return otThreadDetachGracefully(mInstance, ThreadDetachGracefullyHandler, this); +} + +void NcpBase::ThreadDetachGracefullyHandler(void *aContext) +{ + static_cast(aContext)->ThreadDetachGracefullyHandler(); +} + +void NcpBase::ThreadDetachGracefullyHandler(void) +{ + mChangedPropsSet.AddProperty(SPINEL_PROP_NET_LEAVE_GRACEFULLY); + mUpdateChangedPropsTask.Post(); +} + // ---------------------------------------------------------------------------- // MARK: Property/Status Changed // ---------------------------------------------------------------------------- diff --git a/src/posix/platform/README_RCP_CAPS_DIAG.md b/src/posix/platform/README_RCP_CAPS_DIAG.md index 73322ad24..faf9909e8 100644 --- a/src/posix/platform/README_RCP_CAPS_DIAG.md +++ b/src/posix/platform/README_RCP_CAPS_DIAG.md @@ -6,10 +6,50 @@ This module provides diag commands for checking RCP capabilities. ## Command List +- [capflags](#capflags) - [spinel](#spinel) +- [srcmatchtable](#srcmatchtable) ## Command Details +### capflags + +Check RCP's radio and spinel capbility flags. + +```bash +> diag rcpcaps capflags + +Radio Capbility Flags : + +Thread Version >= 1.1 : +RADIO_CAPS_ACK_TIMEOUT ------------------------------------ OK +RADIO_CAPS_TRANSMIT_RETRIES ------------------------------- OK +RADIO_CAPS_CSMA_BACKOFF ----------------------------------- OK + +Thread Version >= 1.2 : +RADIO_CAPS_TRANSMIT_SEC ----------------------------------- OK +RADIO_CAPS_TRANSMIT_TIMING -------------------------------- OK + +Utils : +RADIO_CAPS_ENERGY_SCAN ------------------------------------ OK +RADIO_CAPS_SLEEP_TO_TX ------------------------------------ NotSupported +RADIO_CAPS_RECEIVE_TIMING --------------------------------- NotSupported +RADIO_CAPS_RX_ON_WHEN_IDLE -------------------------------- NotSupported + +Spinel Capbility Flags : + +Basic : +SPINEL_CAPS_CONFIG_RADIO ---------------------------------- OK +SPINEL_CAPS_MAC_RAW --------------------------------------- OK +SPINEL_CAPS_RCP_API_VERSION ------------------------------- OK + +Utils : +SPINEL_CAPS_OPENTHREAD_LOG_METADATA ----------------------- NotSupported +SPINEL_CAPS_RCP_MIN_HOST_API_VERSION ---------------------- OK +SPINEL_CAPS_RCP_RESET_TO_BOOTLOADER ----------------------- NotSupported +Done +``` + ### spinel Check which Spinel commands RCP supports. @@ -19,14 +59,68 @@ Check which Spinel commands RCP supports. Basic : PROP_VALUE_GET CAPS --------------------------------------- OK +PROP_VALUE_GET PROTOCOL_VERSION --------------------------- OK +PROP_VALUE_GET RADIO_CAPS --------------------------------- OK +PROP_VALUE_GET RCP_API_VERSION ---------------------------- OK +PROP_VALUE_GET NCP_VERSION -------------------------------- OK Thread Version >= 1.1 : PROP_VALUE_SET PHY_CHAN ----------------------------------- OK +PROP_VALUE_SET PHY_ENABLED -------------------------------- OK +PROP_VALUE_SET MAC_15_4_PANID ----------------------------- OK +PROP_VALUE_SET MAC_15_4_LADDR ----------------------------- OK +PROP_VALUE_SET MAC_15_4_SADDR ----------------------------- OK +PROP_VALUE_SET MAC_RAW_STREAM_ENABLED --------------------- OK +PROP_VALUE_SET MAC_SCAN_MASK ------------------------------ OK +PROP_VALUE_SET MAC_SCAN_PERIOD ---------------------------- OK +PROP_VALUE_SET MAC_SCAN_STATE ----------------------------- OK +PROP_VALUE_SET MAC_SRC_MATCH_ENABLED ---------------------- OK +PROP_VALUE_SET MAC_SRC_MATCH_SHORT_ADDRESSES -------------- OK +PROP_VALUE_SET MAC_SRC_MATCH_EXTENDED_ADDRESSES ----------- OK +PROP_VALUE_GET HWADDR ------------------------------------- OK +PROP_VALUE_GET PHY_CHAN_PREFERRED ------------------------- OK +PROP_VALUE_GET PHY_CHAN_SUPPORTED ------------------------- OK +PROP_VALUE_GET PHY_RSSI ----------------------------------- OK +PROP_VALUE_GET PHY_RX_SENSITIVITY ------------------------- OK +PROP_VALUE_INSERT MAC_SRC_MATCH_SHORT_ADDRESSES ----------- OK +PROP_VALUE_INSERT MAC_SRC_MATCH_EXTENDED_ADDRESSES -------- OK +PROP_VALUE_REMOVE MAC_SRC_MATCH_SHORT_ADDRESSES ----------- OK +PROP_VALUE_REMOVE MAC_SRC_MATCH_EXTENDED_ADDRESSES -------- OK Thread Version >= 1.2 : PROP_VALUE_SET ENH_ACK_PROBING ---------------------------- NotImplemented +PROP_VALUE_SET RCP_MAC_FRAME_COUNTER ---------------------- OK +PROP_VALUE_SET RCP_MAC_KEY -------------------------------- OK +PROP_VALUE_GET CSL_ACCURACY ------------------------------- OK +PROP_VALUE_GET CSL_UNCERTAINTY ---------------------------- OK +PROP_VALUE_GET TIMESTAMP ---------------------------------- OK -Optional : +Utils : +PROP_VALUE_SET MAC_PROMISCUOUS_MODE ----------------------- OK PROP_VALUE_GET PHY_CCA_THRESHOLD -------------------------- OK +PROP_VALUE_GET PHY_FEM_LNA_GAIN --------------------------- OK +PROP_VALUE_GET PHY_REGION_CODE ---------------------------- OK +PROP_VALUE_GET PHY_TX_POWER ------------------------------- OK +PROP_VALUE_GET RADIO_COEX_ENABLE -------------------------- OK +PROP_VALUE_GET RADIO_COEX_METRICS ------------------------- OK +PROP_VALUE_GET RCP_MIN_HOST_API_VERSION ------------------- OK +PROP_VALUE_SET PHY_CCA_THRESHOLD -------------------------- OK +PROP_VALUE_SET PHY_CHAN_MAX_POWER ------------------------- OK +PROP_VALUE_SET PHY_CHAN_TARGET_POWER ---------------------- OK +PROP_VALUE_SET PHY_FEM_LNA_GAIN --------------------------- OK +PROP_VALUE_SET PHY_REGION_CODE ---------------------------- OK +PROP_VALUE_SET PHY_TX_POWER ------------------------------- OK +PROP_VALUE_SET RADIO_COEX_ENABLE -------------------------- OK +Done +``` + +### srcmatchtable + +Check the source match table size supported by the RCP. + +```bash +> diag rcpcaps srcmatchtable +ShortSrcMatchTableSize ------------------------------------ 128 +ExtendedSrcMatchTableSize --------------------------------- 128 Done ``` diff --git a/src/posix/platform/include/openthread/openthread-system.h b/src/posix/platform/include/openthread/openthread-system.h index 460016e45..3c72ad0b5 100644 --- a/src/posix/platform/include/openthread/openthread-system.h +++ b/src/posix/platform/include/openthread/openthread-system.h @@ -97,6 +97,23 @@ typedef struct otPlatformConfig ///< the type to the app layer. } otPlatformConfig; +/** + * Represents the platform spinel driver structure. + * + */ +typedef struct otSpinelDriver otSpinelDriver; + +/** + * Gets the instance of the spinel driver; + * + * @note This API is used for external projects to get the instance of `SpinelDriver` to customize + * different spinel handlings. + * + * @returns A pointer to the spinel driver instance. + * + */ +otSpinelDriver *otSysGetSpinelDriver(void); + /** * Initializes the co-processor and the spinel driver. * diff --git a/src/posix/platform/multicast_routing.cpp b/src/posix/platform/multicast_routing.cpp index 864f984d4..ffac62157 100644 --- a/src/posix/platform/multicast_routing.cpp +++ b/src/posix/platform/multicast_routing.cpp @@ -310,7 +310,7 @@ otError MulticastRoutingManager::AddMulticastForwardingCache(const Ip6::Address } else { - VerifyOrExit(!aSrcAddr.IsLinkLocal(), error = OT_ERROR_NONE); + VerifyOrExit(!aSrcAddr.IsLinkLocalUnicast(), error = OT_ERROR_NONE); VerifyOrExit(aSrcAddr.GetPrefix() != AsCoreType(otThreadGetMeshLocalPrefix(gInstance)), error = OT_ERROR_NONE); // Forward multicast traffic from Thread to Backbone if multicast scope > kRealmLocalScope // TODO: (MLR) allow scope configuration of outbound multicast routing diff --git a/src/posix/platform/netif.cpp b/src/posix/platform/netif.cpp index e0a4c49b8..12bee4256 100644 --- a/src/posix/platform/netif.cpp +++ b/src/posix/platform/netif.cpp @@ -483,7 +483,7 @@ static void UpdateUnicastLinux(otInstance *aInstance, const otIp6AddressInfo &aA #endif { #if OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC > 0 - static constexpr kLinkLocalScope = 2; + static constexpr uint8_t kLinkLocalScope = 2; if (aAddressInfo.mScope > kLinkLocalScope) { @@ -652,7 +652,8 @@ template otError AddRoute(const uint8_t (&aAddress)[N], uint8_t aPref char buf[kBufSize]; } req{}; unsigned int netifIdx = otSysGetThreadNetifIndex(); - otError error = OT_ERROR_NONE; + char addrStrBuf[INET6_ADDRSTRLEN]; + otError error = OT_ERROR_NONE; static_assert(N == sizeof(in6_addr) || N == sizeof(in_addr), "aAddress should be 4 octets or 16 octets"); @@ -680,11 +681,18 @@ template otError AddRoute(const uint8_t (&aAddress)[N], uint8_t aPref AddRtAttrUint32(&req.header, sizeof(req), RTA_PRIORITY, aPriority); AddRtAttrUint32(&req.header, sizeof(req), RTA_OIF, netifIdx); + inet_ntop(req.msg.rtm_family, aAddress, addrStrBuf, sizeof(addrStrBuf)); + if (send(sNetlinkFd, &req, sizeof(req), 0) < 0) { + LogInfo("Failed to send request#%u to add route %s/%u", sNetlinkSequence, addrStrBuf, aPrefixLen); VerifyOrExit(errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK, error = OT_ERROR_BUSY); DieNow(OT_EXIT_ERROR_ERRNO); } + else + { + LogInfo("Sent request#%u to add route %s/%u", sNetlinkSequence, addrStrBuf, aPrefixLen); + } exit: return error; } @@ -699,7 +707,8 @@ template otError DeleteRoute(const uint8_t (&aAddress)[N], uint8_t aP char buf[kBufSize]; } req{}; unsigned int netifIdx = otSysGetThreadNetifIndex(); - otError error = OT_ERROR_NONE; + char addrStrBuf[INET6_ADDRSTRLEN]; + otError error = OT_ERROR_NONE; static_assert(N == sizeof(in6_addr) || N == sizeof(in_addr), "aAddress should be 4 octets or 16 octets"); @@ -726,11 +735,18 @@ template otError DeleteRoute(const uint8_t (&aAddress)[N], uint8_t aP AddRtAttr(reinterpret_cast(&req), sizeof(req), RTA_DST, &aAddress, sizeof(aAddress)); AddRtAttrUint32(&req.header, sizeof(req), RTA_OIF, netifIdx); + inet_ntop(req.msg.rtm_family, aAddress, addrStrBuf, sizeof(addrStrBuf)); + if (send(sNetlinkFd, &req, sizeof(req), 0) < 0) { + LogInfo("Failed to send request#%u to delete route %s/%u", sNetlinkSequence, addrStrBuf, aPrefixLen); VerifyOrExit(errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK, error = OT_ERROR_BUSY); DieNow(OT_EXIT_ERROR_ERRNO); } + else + { + LogInfo("Sent request#%u to delete route %s/%u", sNetlinkSequence, addrStrBuf, aPrefixLen); + } exit: return error; @@ -1340,6 +1356,7 @@ static void processNetifAddrEvent(otInstance *aInstance, struct nlmsghdr *aNetli netAddr.mPrefixLength = ifaddr->ifa_prefixlen; error = otIp6AddUnicastAddress(aInstance, &netAddr); + error = (error == OT_ERROR_INVALID_ARGS) ? OT_ERROR_NONE : error; } else { diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index eabeaf6bb..ba63d1b8d 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -498,11 +498,55 @@ otError otPlatRadioGetCoexMetrics(otInstance *aInstance, otRadioCoexMetrics *aCo #endif #if OPENTHREAD_CONFIG_DIAG_ENABLE -otError otPlatDiagProcess(otInstance *aInstance, - uint8_t aArgsLength, - char *aArgs[], - char *aOutput, - size_t aOutputMaxLen) +static otPlatDiagOutputCallback sDiagOutputCallback = nullptr; +static void *sDiagCallbackContext = nullptr; +static char *sDiagOutput = nullptr; +static uint16_t sDiagOutputLen = 0; + +static void handleDiagOutput(const char *aFormat, va_list aArguments, void *aContext) +{ + OT_UNUSED_VARIABLE(aContext); + int charsWritten; + + VerifyOrExit((sDiagOutput != nullptr) && (sDiagOutputLen > 0)); + charsWritten = vsnprintf(sDiagOutput, sDiagOutputLen, aFormat, aArguments); + VerifyOrExit(charsWritten > 0); + charsWritten = (sDiagOutputLen <= charsWritten) ? sDiagOutputLen : charsWritten; + sDiagOutput += charsWritten; + sDiagOutputLen -= charsWritten; + +exit: + return; +} + +static void setDiagOutput(char *aOutput, size_t aSize) +{ + sDiagOutput = aOutput; + sDiagOutputLen = static_cast(aSize); + GetRadioSpinel().SetDiagOutputCallback(handleDiagOutput, nullptr); +} + +static void freeDiagOutput(void) +{ + sDiagOutput = nullptr; + sDiagOutputLen = 0; + GetRadioSpinel().SetDiagOutputCallback(sDiagOutputCallback, sDiagCallbackContext); +} + +void otPlatDiagSetOutputCallback(otInstance *aInstance, otPlatDiagOutputCallback aCallback, void *aContext) +{ + OT_UNUSED_VARIABLE(aInstance); + + sDiagOutputCallback = aCallback; + sDiagCallbackContext = aContext; + + GetRadioSpinel().SetDiagOutputCallback(aCallback, aContext); +#if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE + GetRcpCapsDiag().SetDiagOutputCallback(aCallback, aContext); +#endif +} + +otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) { // deliver the platform specific diags commands to radio only ncp. OT_UNUSED_VARIABLE(aInstance); @@ -513,7 +557,7 @@ otError otPlatDiagProcess(otInstance *aInstance, #if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE if (strcmp(aArgs[0], "rcpcaps") == 0) { - return GetRcpCapsDiag().DiagProcess(aArgs, aArgsLength, aOutput, aOutputMaxLen); + return GetRcpCapsDiag().DiagProcess(aArgs, aArgsLength); } #endif @@ -522,12 +566,12 @@ otError otPlatDiagProcess(otInstance *aInstance, cur += snprintf(cur, static_cast(end - cur), "%s ", aArgs[index]); } - return GetRadioSpinel().PlatDiagProcess(cmd, aOutput, aOutputMaxLen); + return GetRadioSpinel().PlatDiagProcess(cmd); } void otPlatDiagModeSet(bool aMode) { - SuccessOrExit(GetRadioSpinel().PlatDiagProcess(aMode ? "start" : "stop", nullptr, 0)); + SuccessOrExit(GetRadioSpinel().PlatDiagProcess(aMode ? "start" : "stop")); GetRadioSpinel().SetDiagEnabled(aMode); exit: @@ -541,7 +585,7 @@ void otPlatDiagTxPowerSet(int8_t aTxPower) char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; snprintf(cmd, sizeof(cmd), "power %d", aTxPower); - SuccessOrExit(GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0)); + SuccessOrExit(GetRadioSpinel().PlatDiagProcess(cmd)); exit: return; @@ -552,7 +596,7 @@ void otPlatDiagChannelSet(uint8_t aChannel) char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; snprintf(cmd, sizeof(cmd), "channel %d", aChannel); - SuccessOrExit(GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0)); + SuccessOrExit(GetRadioSpinel().PlatDiagProcess(cmd)); exit: return; @@ -564,7 +608,7 @@ otError otPlatDiagGpioSet(uint32_t aGpio, bool aValue) char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; snprintf(cmd, sizeof(cmd), "gpio set %d %d", aGpio, aValue); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0)); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); exit: return error; @@ -577,12 +621,16 @@ otError otPlatDiagGpioGet(uint32_t aGpio, bool *aValue) char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; char *str; + setDiagOutput(output, sizeof(output)); + snprintf(cmd, sizeof(cmd), "gpio get %d", aGpio); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, output, sizeof(output))); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); VerifyOrExit((str = strtok(output, "\r")) != nullptr, error = OT_ERROR_FAILED); *aValue = static_cast(atoi(str)); exit: + freeDiagOutput(); + return error; } @@ -592,7 +640,7 @@ otError otPlatDiagGpioSetMode(uint32_t aGpio, otGpioMode aMode) char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; snprintf(cmd, sizeof(cmd), "gpio mode %d %s", aGpio, aMode == OT_GPIO_MODE_INPUT ? "in" : "out"); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0)); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); exit: return error; @@ -605,8 +653,10 @@ otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode) char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; char *str; + setDiagOutput(output, sizeof(output)); + snprintf(cmd, sizeof(cmd), "gpio mode %d", aGpio); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, output, sizeof(output))); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); VerifyOrExit((str = strtok(output, "\r")) != nullptr, error = OT_ERROR_FAILED); if (strcmp(str, "in") == 0) @@ -623,6 +673,8 @@ otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode) } exit: + freeDiagOutput(); + return error; } @@ -648,8 +700,10 @@ otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance, assert((aTargetPower != nullptr) && (aActualPower != nullptr) && (aRawPowerSetting != nullptr) && (aRawPowerSettingLength != nullptr)); + setDiagOutput(output, sizeof(output)); + snprintf(cmd, sizeof(cmd), "powersettings %d", aChannel); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, output, sizeof(output))); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); snprintf(fmt, sizeof(fmt), "TargetPower(0.01dBm): %%d\r\nActualPower(0.01dBm): %%d\r\nRawPowerSetting: %%%us\r\n", kRawPowerStringSize); VerifyOrExit(sscanf(output, fmt, &targetPower, &actualPower, rawPowerSetting) == 3, error = OT_ERROR_FAILED); @@ -659,6 +713,8 @@ otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance, *aActualPower = static_cast(actualPower); exit: + freeDiagOutput(); + return error; } @@ -682,7 +738,7 @@ otError otPlatDiagRadioSetRawPowerSetting(otInstance *aInstance, VerifyOrExit(nbytes < static_cast(sizeof(cmd)), error = OT_ERROR_INVALID_ARGS); } - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0)); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); exit: return error; @@ -700,12 +756,16 @@ otError otPlatDiagRadioGetRawPowerSetting(otInstance *aInstance, assert((aRawPowerSetting != nullptr) && (aRawPowerSettingLength != nullptr)); + setDiagOutput(output, sizeof(output)); + snprintf(cmd, sizeof(cmd), "rawpowersetting"); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, output, sizeof(output))); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); VerifyOrExit((str = strtok(output, "\r")) != nullptr, error = OT_ERROR_FAILED); SuccessOrExit(error = ot::Utils::CmdLineParser::ParseAsHexString(str, *aRawPowerSettingLength, aRawPowerSetting)); exit: + freeDiagOutput(); + return error; } @@ -717,7 +777,7 @@ otError otPlatDiagRadioRawPowerSettingEnable(otInstance *aInstance, bool aEnable char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; snprintf(cmd, sizeof(cmd), "rawpowersetting %s", aEnable ? "enable" : "disable"); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0)); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); exit: return error; @@ -731,7 +791,7 @@ otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable) char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; snprintf(cmd, sizeof(cmd), "cw %s", aEnable ? "start" : "stop"); - SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0)); + SuccessOrExit(error = GetRadioSpinel().PlatDiagProcess(cmd)); exit: return error; @@ -744,7 +804,7 @@ otError otPlatDiagRadioTransmitStream(otInstance *aInstance, bool aEnable) char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; snprintf(cmd, sizeof(cmd), "stream %s", aEnable ? "start" : "stop"); - return GetRadioSpinel().PlatDiagProcess(cmd, nullptr, 0); + return GetRadioSpinel().PlatDiagProcess(cmd); } void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError) diff --git a/src/posix/platform/rcp_caps_diag.cpp b/src/posix/platform/rcp_caps_diag.cpp index 79959b210..e972f0e48 100644 --- a/src/posix/platform/rcp_caps_diag.cpp +++ b/src/posix/platform/rcp_caps_diag.cpp @@ -37,11 +37,44 @@ namespace Posix { aCategory, aCommand, aKey, &RcpCapsDiag::HandleSpinelCommand \ } -template <> otError RcpCapsDiag::HandleSpinelCommand(void) +template <> otError RcpCapsDiag::HandleSpinelCommand(void) { - int8_t ccaThreshold; + static constexpr uint8_t kCapsBufferSize = 100; + uint8_t capsBuffer[kCapsBufferSize]; + spinel_size_t capsLength = sizeof(capsBuffer); + + return mRadioSpinel.Get(SPINEL_PROP_CAPS, SPINEL_DATATYPE_DATA_S, capsBuffer, &capsLength); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + unsigned int versionMajor; + unsigned int versionMinor; + + return mRadioSpinel.Get(SPINEL_PROP_PROTOCOL_VERSION, (SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S), + &versionMajor, &versionMinor); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + unsigned int radioCaps; + + return mRadioSpinel.Get(SPINEL_PROP_RADIO_CAPS, SPINEL_DATATYPE_UINT_PACKED_S, &radioCaps); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + unsigned int rcpApiVersion; + + return mRadioSpinel.Get(SPINEL_PROP_RCP_API_VERSION, SPINEL_DATATYPE_UINT_PACKED_S, &rcpApiVersion); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr uint16_t kVersionStringSize = 128; + char mVersion[kVersionStringSize]; - return mRadioSpinel.GetCcaEnergyDetectThreshold(ccaThreshold); + return mRadioSpinel.Get(SPINEL_PROP_NCP_VERSION, SPINEL_DATATYPE_UTF8_S, mVersion, sizeof(mVersion)); } template <> otError RcpCapsDiag::HandleSpinelCommand(void) @@ -51,51 +84,384 @@ template <> otError RcpCapsDiag::HandleSpinelCommand otError RcpCapsDiag::HandleSpinelCommand(void) +template <> otError RcpCapsDiag::HandleSpinelCommand(void) { - static constexpr uint8_t kCapsBufferSize = 100; - uint8_t capsBuffer[kCapsBufferSize]; - spinel_size_t capsLength = sizeof(capsBuffer); + return mRadioSpinel.Set(SPINEL_PROP_PHY_ENABLED, SPINEL_DATATYPE_BOOL_S, true /* aEnable*/); +} - return mRadioSpinel.Get(SPINEL_PROP_CAPS, SPINEL_DATATYPE_DATA_S, capsBuffer, &capsLength); +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr uint16_t kPanId = 0x1234; + + return mRadioSpinel.SetPanId(kPanId); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr otExtAddress kExtAddress = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; + + return mRadioSpinel.Set(SPINEL_PROP_MAC_15_4_LADDR, SPINEL_DATATYPE_EUI64_S, kExtAddress.m8); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr uint16_t kShortAddress = 0x1100; + + return mRadioSpinel.Set(SPINEL_PROP_MAC_15_4_SADDR, SPINEL_DATATYPE_UINT16_S, kShortAddress); +} + +template <> +otError RcpCapsDiag::HandleSpinelCommand(void) +{ + return mRadioSpinel.Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, true); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr uint8_t kScanChannel = 20; + + return mRadioSpinel.Set(SPINEL_PROP_MAC_SCAN_MASK, SPINEL_DATATYPE_DATA_S, &kScanChannel, sizeof(uint8_t)); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr uint16_t kScanDuration = 1; + + return mRadioSpinel.Set(SPINEL_PROP_MAC_SCAN_PERIOD, SPINEL_DATATYPE_UINT16_S, kScanDuration); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + return mRadioSpinel.Set(SPINEL_PROP_MAC_SCAN_STATE, SPINEL_DATATYPE_UINT8_S, SPINEL_SCAN_STATE_ENERGY); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + return mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_ENABLED, SPINEL_DATATYPE_BOOL_S, true); +} + +template <> +otError RcpCapsDiag::HandleSpinelCommand(void) +{ + return mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, nullptr); +} + +template <> +otError RcpCapsDiag::HandleSpinelCommand(void) +{ + return mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, nullptr); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + otExtAddress ieeeEui64; + + return mRadioSpinel.Get(SPINEL_PROP_HWADDR, SPINEL_DATATYPE_EUI64_S, ieeeEui64.m8); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr uint8_t kChannelMaskBufferSize = 32; + uint8_t maskBuffer[kChannelMaskBufferSize]; + spinel_size_t maskLength = sizeof(maskBuffer); + + return mRadioSpinel.Get(SPINEL_PROP_PHY_CHAN_PREFERRED, SPINEL_DATATYPE_DATA_S, maskBuffer, &maskLength); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr uint8_t kChannelMaskBufferSize = 32; + uint8_t maskBuffer[kChannelMaskBufferSize]; + spinel_size_t maskLength = sizeof(maskBuffer); + + return mRadioSpinel.Get(SPINEL_PROP_PHY_CHAN_SUPPORTED, SPINEL_DATATYPE_DATA_S, maskBuffer, &maskLength); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + int8_t rssi; + + return mRadioSpinel.Get(SPINEL_PROP_PHY_RSSI, SPINEL_DATATYPE_INT8_S, &rssi); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + int8_t rxSensitivity; + + return mRadioSpinel.Get(SPINEL_PROP_PHY_RX_SENSITIVITY, SPINEL_DATATYPE_INT8_S, &rxSensitivity); +} + +template <> +otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr uint16_t kShortAddress = 0x1122; + + return mRadioSpinel.Insert(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, kShortAddress); +} + +template <> +otError RcpCapsDiag::HandleSpinelCommand( + void) +{ + static constexpr otExtAddress kExtAddress = {{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}}; + + return mRadioSpinel.Insert(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, kExtAddress.m8); +} + +template <> +otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr uint16_t kShortAddress = 0x1122; + + return mRadioSpinel.Remove(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, kShortAddress); +} + +template <> +otError RcpCapsDiag::HandleSpinelCommand( + void) +{ + static constexpr otExtAddress extAddress = {{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}}; + + return mRadioSpinel.Remove(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, extAddress.m8); } template <> otError RcpCapsDiag::HandleSpinelCommand(void) { - uint16_t shortAddress = 0x1122; - otExtAddress extAddress = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; - uint8_t flags = SPINEL_THREAD_LINK_METRIC_PDU_COUNT | SPINEL_THREAD_LINK_METRIC_LQI | - SPINEL_THREAD_LINK_METRIC_LINK_MARGIN | SPINEL_THREAD_LINK_METRIC_RSSI; + static constexpr uint16_t kShortAddress = 0x1122; + static constexpr otExtAddress kExtAddress = {{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}}; + static constexpr uint8_t kFlags = SPINEL_THREAD_LINK_METRIC_PDU_COUNT | SPINEL_THREAD_LINK_METRIC_LQI | + SPINEL_THREAD_LINK_METRIC_LINK_MARGIN | SPINEL_THREAD_LINK_METRIC_RSSI; return mRadioSpinel.Set(SPINEL_PROP_RCP_ENH_ACK_PROBING, - SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT8_S, shortAddress, - extAddress.m8, flags); + SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT8_S, kShortAddress, + kExtAddress.m8, kFlags); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr uint32_t kMacFrameCounter = 1; + + return mRadioSpinel.Set(SPINEL_PROP_RCP_MAC_FRAME_COUNTER, SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_BOOL_S, + kMacFrameCounter, true /*aSetIfLarger*/); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr uint8_t keyIdMode1 = 1 << 3; + static constexpr uint8_t keyId = 100; + otMacKeyMaterial prevKey; + otMacKeyMaterial curKey; + otMacKeyMaterial nextKey; + + memset(prevKey.mKeyMaterial.mKey.m8, 0x11, OT_MAC_KEY_SIZE); + memset(curKey.mKeyMaterial.mKey.m8, 0x22, OT_MAC_KEY_SIZE); + memset(nextKey.mKeyMaterial.mKey.m8, 0x33, OT_MAC_KEY_SIZE); + return mRadioSpinel.SetMacKey(keyIdMode1, keyId, &prevKey, &curKey, &nextKey); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + uint8_t accuracy; + + return mRadioSpinel.Get(SPINEL_PROP_RCP_CSL_ACCURACY, SPINEL_DATATYPE_UINT8_S, &accuracy); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + uint8_t uncertainty; + + return mRadioSpinel.Get(SPINEL_PROP_RCP_CSL_UNCERTAINTY, SPINEL_DATATYPE_UINT8_S, &uncertainty); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + uint64_t remoteTimestamp = 0; + uint8_t buffer[sizeof(remoteTimestamp)]; + spinel_ssize_t packed; + + packed = spinel_datatype_pack(buffer, sizeof(buffer), SPINEL_DATATYPE_UINT64_S, remoteTimestamp); + return mRadioSpinel.GetWithParam(SPINEL_PROP_RCP_TIMESTAMP, buffer, static_cast(packed), + SPINEL_DATATYPE_UINT64_S, &remoteTimestamp); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + return mRadioSpinel.SetPromiscuous(false /* aEnable*/); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + int8_t ccaThreshold; + + return mRadioSpinel.Get(SPINEL_PROP_PHY_CCA_THRESHOLD, SPINEL_DATATYPE_INT8_S, &ccaThreshold); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + int8_t gain; + + return mRadioSpinel.Get(SPINEL_PROP_PHY_FEM_LNA_GAIN, SPINEL_DATATYPE_INT8_S, &gain); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + uint16_t regionCode; + + return mRadioSpinel.Get(SPINEL_PROP_PHY_REGION_CODE, SPINEL_DATATYPE_UINT16_S, ®ionCode); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + int8_t power; + + return mRadioSpinel.Get(SPINEL_PROP_PHY_TX_POWER, SPINEL_DATATYPE_INT8_S, &power); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + bool enabled; + + return mRadioSpinel.Get(SPINEL_PROP_RADIO_COEX_ENABLE, SPINEL_DATATYPE_BOOL_S, &enabled); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + otRadioCoexMetrics coexMetrics; + + return mRadioSpinel.GetCoexMetrics(coexMetrics); +} + +template <> +otError RcpCapsDiag::HandleSpinelCommand(void) +{ + unsigned int minHostRcpApiVersion; + + return mRadioSpinel.Get(SPINEL_PROP_RCP_MIN_HOST_API_VERSION, SPINEL_DATATYPE_UINT_PACKED_S, &minHostRcpApiVersion); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr int8_t kCcaThreshold = -75; + + return mRadioSpinel.Set(SPINEL_PROP_PHY_CCA_THRESHOLD, SPINEL_DATATYPE_INT8_S, kCcaThreshold); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr uint8_t kChannel = 20; + static constexpr uint8_t kMaxPower = 10; + + return mRadioSpinel.Set(SPINEL_PROP_PHY_CHAN_MAX_POWER, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S, kChannel, + kMaxPower); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr uint8_t kChannel = 20; + static constexpr uint16_t kTargetPower = 1000; + + return mRadioSpinel.Set(SPINEL_PROP_PHY_CHAN_TARGET_POWER, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S, + kChannel, kTargetPower); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr int8_t kFemLnaGain = 0; + + return mRadioSpinel.Set(SPINEL_PROP_PHY_FEM_LNA_GAIN, SPINEL_DATATYPE_INT8_S, kFemLnaGain); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr uint16_t kRegionCode = 0x5757; + + return mRadioSpinel.Set(SPINEL_PROP_PHY_REGION_CODE, SPINEL_DATATYPE_UINT16_S, kRegionCode); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + static constexpr int8_t kTransmitPower = 10; + + return mRadioSpinel.Set(SPINEL_PROP_PHY_TX_POWER, SPINEL_DATATYPE_INT8_S, kTransmitPower); +} + +template <> otError RcpCapsDiag::HandleSpinelCommand(void) +{ + return mRadioSpinel.Set(SPINEL_PROP_RADIO_COEX_ENABLE, SPINEL_DATATYPE_BOOL_S, true /* aEnabled*/); } const struct RcpCapsDiag::SpinelEntry RcpCapsDiag::sSpinelEntries[] = { // Basic Spinel commands SPINEL_ENTRY(kCategoryBasic, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_CAPS), + SPINEL_ENTRY(kCategoryBasic, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PROTOCOL_VERSION), + SPINEL_ENTRY(kCategoryBasic, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RADIO_CAPS), + SPINEL_ENTRY(kCategoryBasic, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RCP_API_VERSION), + SPINEL_ENTRY(kCategoryBasic, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_NCP_VERSION), // Thread Version >= 1.1 SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_CHAN), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_ENABLED), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_15_4_PANID), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_15_4_LADDR), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_15_4_SADDR), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_RAW_STREAM_ENABLED), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SCAN_MASK), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SCAN_PERIOD), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SCAN_STATE), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SRC_MATCH_ENABLED), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_HWADDR), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_CHAN_PREFERRED), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_CHAN_SUPPORTED), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_RSSI), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_RX_SENSITIVITY), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_INSERT, SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_INSERT, SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_REMOVE, SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES), + SPINEL_ENTRY(kCategoryThread1_1, SPINEL_CMD_PROP_VALUE_REMOVE, SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES), // Thread Version >= 1.2 SPINEL_ENTRY(kCategoryThread1_2, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_RCP_ENH_ACK_PROBING), - - // Optional Spinel commands - SPINEL_ENTRY(kCategoryOptional, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_CCA_THRESHOLD), + SPINEL_ENTRY(kCategoryThread1_2, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_RCP_MAC_FRAME_COUNTER), + SPINEL_ENTRY(kCategoryThread1_2, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_RCP_MAC_KEY), + SPINEL_ENTRY(kCategoryThread1_2, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RCP_CSL_ACCURACY), + SPINEL_ENTRY(kCategoryThread1_2, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RCP_CSL_UNCERTAINTY), + SPINEL_ENTRY(kCategoryThread1_2, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RCP_TIMESTAMP), + + // Utils + SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MAC_PROMISCUOUS_MODE), + SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_CCA_THRESHOLD), + SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_FEM_LNA_GAIN), + SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_REGION_CODE), + SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PHY_TX_POWER), + SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RADIO_COEX_ENABLE), + SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RADIO_COEX_METRICS), + SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_RCP_MIN_HOST_API_VERSION), + SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_CCA_THRESHOLD), + SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_CHAN_MAX_POWER), + SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_CHAN_TARGET_POWER), + SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_FEM_LNA_GAIN), + SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_REGION_CODE), + SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_TX_POWER), + SPINEL_ENTRY(kCategoryUtils, SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_RADIO_COEX_ENABLE), }; -otError RcpCapsDiag::DiagProcess(char *aArgs[], uint8_t aArgsLength, char *aOutput, size_t aOutputMaxLen) +otError RcpCapsDiag::DiagProcess(char *aArgs[], uint8_t aArgsLength) { otError error = OT_ERROR_NONE; VerifyOrExit(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS); - mOutputStart = aOutput; - mOutputEnd = aOutput + aOutputMaxLen; - - if (strcmp(aArgs[1], "spinel") == 0) + if (strcmp(aArgs[1], "capflags") == 0) + { + ProcessCapabilityFlags(); + } + else if (strcmp(aArgs[1], "srcmatchtable") == 0) + { + ProcessSrcMatchTable(); + } + else if (strcmp(aArgs[1], "spinel") == 0) { ProcessSpinel(); } @@ -104,9 +470,6 @@ otError RcpCapsDiag::DiagProcess(char *aArgs[], uint8_t aArgsLength, char *aOutp error = OT_ERROR_INVALID_COMMAND; } - mOutputStart = nullptr; - mOutputEnd = nullptr; - exit: return error; } @@ -137,22 +500,221 @@ void RcpCapsDiag::TestSpinelCommands(Category aCategory) } } +void RcpCapsDiag::SetDiagOutputCallback(otPlatDiagOutputCallback aCallback, void *aContext) +{ + mOutputCallback = aCallback; + mOutputContext = aContext; +} + +void RcpCapsDiag::ProcessCapabilityFlags(void) +{ + TestRadioCapbilityFlags(); + TestSpinelCapbilityFlags(); +} + +void RcpCapsDiag::TestRadioCapbilityFlags(void) +{ + static constexpr uint32_t kRadioThread11Flags[] = {OT_RADIO_CAPS_ACK_TIMEOUT, OT_RADIO_CAPS_TRANSMIT_RETRIES, + OT_RADIO_CAPS_CSMA_BACKOFF}; + static constexpr uint32_t kRadioThread12Flags[] = {OT_RADIO_CAPS_TRANSMIT_SEC, OT_RADIO_CAPS_TRANSMIT_TIMING}; + static constexpr uint32_t kRadioUtilsFlags[] = {OT_RADIO_CAPS_ENERGY_SCAN, OT_RADIO_CAPS_SLEEP_TO_TX, + OT_RADIO_CAPS_RECEIVE_TIMING, OT_RADIO_CAPS_RX_ON_WHEN_IDLE}; + otError error; + unsigned int radioCaps; + + SuccessOrExit(error = mRadioSpinel.Get(SPINEL_PROP_RADIO_CAPS, SPINEL_DATATYPE_UINT_PACKED_S, &radioCaps)); + + Output("\r\nRadio Capbility Flags :\r\n"); + + OutputRadioCapFlags(kCategoryThread1_1, static_cast(radioCaps), kRadioThread11Flags, + OT_ARRAY_LENGTH(kRadioThread11Flags)); + OutputRadioCapFlags(kCategoryThread1_2, static_cast(radioCaps), kRadioThread12Flags, + OT_ARRAY_LENGTH(kRadioThread12Flags)); + OutputRadioCapFlags(kCategoryUtils, static_cast(radioCaps), kRadioUtilsFlags, + OT_ARRAY_LENGTH(kRadioUtilsFlags)); + +exit: + if (error != OT_ERROR_NONE) + { + Output("Failed to get radio capability flags: %s", otThreadErrorToString(error)); + } + + return; +} + +void RcpCapsDiag::OutputRadioCapFlags(Category aCategory, + uint32_t aRadioCaps, + const uint32_t *aFlags, + uint16_t aNumbFlags) +{ + Output("\r\n%s :\r\n", CategoryToString(aCategory)); + for (uint16_t i = 0; i < aNumbFlags; i++) + { + OutputFormat(RadioCapbilityToString(aFlags[i]), SupportToString((aRadioCaps & aFlags[i]) > 0)); + } +} + +void RcpCapsDiag::TestSpinelCapbilityFlags(void) +{ + static constexpr uint8_t kCapsBufferSize = 100; + static constexpr uint32_t kSpinelBasicFlags[] = {SPINEL_CAP_CONFIG_RADIO, SPINEL_CAP_MAC_RAW, + SPINEL_CAP_RCP_API_VERSION}; + static constexpr uint32_t kSpinelUtilsFlags[] = { + SPINEL_CAP_OPENTHREAD_LOG_METADATA, SPINEL_CAP_RCP_MIN_HOST_API_VERSION, SPINEL_CAP_RCP_RESET_TO_BOOTLOADER}; + otError error; + uint8_t capsBuffer[kCapsBufferSize]; + spinel_size_t capsLength = sizeof(capsBuffer); + + SuccessOrExit(error = mRadioSpinel.Get(SPINEL_PROP_CAPS, SPINEL_DATATYPE_DATA_S, capsBuffer, &capsLength)); + + Output("\r\nSpinel Capbility Flags :\r\n"); + + OutputSpinelCapFlags(kCategoryBasic, capsBuffer, capsLength, kSpinelBasicFlags, OT_ARRAY_LENGTH(kSpinelBasicFlags)); + OutputSpinelCapFlags(kCategoryUtils, capsBuffer, capsLength, kSpinelUtilsFlags, OT_ARRAY_LENGTH(kSpinelUtilsFlags)); + +exit: + if (error != OT_ERROR_NONE) + { + Output("Failed to get Spinel capbility flags: %s", otThreadErrorToString(error)); + } + + return; +} + +void RcpCapsDiag::OutputSpinelCapFlags(Category aCategory, + const uint8_t *aCapsData, + spinel_size_t aCapsLength, + const uint32_t *aFlags, + uint16_t aNumbFlags) +{ + static constexpr uint8_t kCapsNameSize = 40; + char capName[kCapsNameSize]; + + Output("\r\n%s :\r\n", CategoryToString(aCategory)); + + for (uint16_t i = 0; i < aNumbFlags; i++) + { + snprintf(capName, sizeof(capName), "SPINEL_CAPS_%s", spinel_capability_to_cstr(aFlags[i])); + OutputFormat(capName, SupportToString(IsSpinelCapabilitySupported(aCapsData, aCapsLength, aFlags[i]))); + } +} + +bool RcpCapsDiag::IsSpinelCapabilitySupported(const uint8_t *aCapsData, spinel_size_t aCapsLength, uint32_t aCapability) +{ + bool ret = false; + + while (aCapsLength > 0) + { + unsigned int capability; + spinel_ssize_t unpacked; + + unpacked = spinel_datatype_unpack(aCapsData, aCapsLength, SPINEL_DATATYPE_UINT_PACKED_S, &capability); + VerifyOrExit(unpacked > 0); + VerifyOrExit(capability != aCapability, ret = true); + + aCapsData += unpacked; + aCapsLength -= static_cast(unpacked); + } + +exit: + return ret; +} + +void RcpCapsDiag::ProcessSrcMatchTable(void) +{ + OutputShortSrcMatchTableSize(); + OutputExtendedSrcMatchTableSize(); +} + +void RcpCapsDiag::OutputShortSrcMatchTableSize(void) +{ + constexpr uint8_t kRouterIdOffset = 10; + constexpr uint8_t kRouterId = 5; + uint16_t num = 0; + uint16_t shortAddress; + + SuccessOrExit(mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_ENABLED, SPINEL_DATATYPE_BOOL_S, true /* aEnable */)); + SuccessOrExit(mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, nullptr)); + + for (num = 0; num < kMaxNumChildren; num++) + { + shortAddress = num | (kRouterId << kRouterIdOffset); + SuccessOrExit( + mRadioSpinel.Insert(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, shortAddress)); + } + +exit: + if (num != 0) + { + IgnoreReturnValue(mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, nullptr)); + IgnoreReturnValue( + mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_ENABLED, SPINEL_DATATYPE_BOOL_S, false /* aEnable */)); + } + + OutputFormat("ShortSrcMatchTableSize", num); +} + +void RcpCapsDiag::OutputExtendedSrcMatchTableSize(void) +{ + otExtAddress extAddress = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + uint16_t num = 0; + + SuccessOrExit(mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_ENABLED, SPINEL_DATATYPE_BOOL_S, true /* aEnable */)); + SuccessOrExit(mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, nullptr)); + + for (num = 0; num < kMaxNumChildren; num++) + { + *reinterpret_cast(extAddress.m8) = num; + SuccessOrExit( + mRadioSpinel.Insert(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, extAddress.m8)); + } + +exit: + if (num != 0) + { + IgnoreReturnValue(mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, nullptr)); + IgnoreReturnValue( + mRadioSpinel.Set(SPINEL_PROP_MAC_SRC_MATCH_ENABLED, SPINEL_DATATYPE_BOOL_S, false /* aEnable */)); + } + + OutputFormat("ExtendedSrcMatchTableSize", num); +} + +void RcpCapsDiag::OutputFormat(const char *aName, const char *aValue) +{ + static constexpr uint8_t kMaxNameLength = 56; + static const char kPadding[] = "----------------------------------------------------------"; + uint16_t actualLength = static_cast(strlen(aName)); + uint16_t paddingOffset = (actualLength > kMaxNameLength) ? kMaxNameLength : actualLength; + + static_assert(kMaxNameLength < sizeof(kPadding), "Padding bytes are too short"); + + Output("%.*s %s %s\r\n", kMaxNameLength, aName, &kPadding[paddingOffset], aValue); +} + +void RcpCapsDiag::OutputFormat(const char *aName, uint32_t aValue) +{ + static constexpr uint16_t kValueLength = 11; + char value[kValueLength]; + + snprintf(value, sizeof(value), "%u", aValue); + OutputFormat(aName, value); +} + void RcpCapsDiag::OutputResult(const SpinelEntry &aEntry, otError error) { static constexpr uint8_t kSpaceLength = 1; static constexpr uint8_t kMaxCommandStringLength = 20; static constexpr uint8_t kMaxKeyStringLength = 35; - static constexpr uint16_t kMaxLength = kMaxCommandStringLength + kMaxKeyStringLength + kSpaceLength; - static const char kPadding[] = "----------------------------------------------------------"; - const char *commandString = spinel_command_to_cstr(aEntry.mCommand); - const char *keyString = spinel_prop_key_to_cstr(aEntry.mKey); - uint16_t actualLength = static_cast(strlen(commandString) + strlen(keyString) + kSpaceLength); - uint16_t paddingOffset = (actualLength > kMaxLength) ? kMaxLength : actualLength; - - static_assert(kMaxLength < sizeof(kPadding), "Padding bytes are too short"); - - Output("%.*s %.*s %s %s\r\n", kMaxCommandStringLength, commandString, kMaxKeyStringLength, keyString, - &kPadding[paddingOffset], otThreadErrorToString(error)); + static constexpr uint16_t kMaxBufferLength = + kMaxCommandStringLength + kMaxKeyStringLength + kSpaceLength + 1 /* size of '\0' */; + char buffer[kMaxBufferLength] = {0}; + const char *commandString = spinel_command_to_cstr(aEntry.mCommand); + const char *keyString = spinel_prop_key_to_cstr(aEntry.mKey); + + snprintf(buffer, sizeof(buffer), "%.*s %.*s", kMaxCommandStringLength, commandString, kMaxKeyStringLength, + keyString); + OutputFormat(buffer, otThreadErrorToString(error)); } void RcpCapsDiag::Output(const char *aFormat, ...) @@ -161,9 +723,9 @@ void RcpCapsDiag::Output(const char *aFormat, ...) va_start(args, aFormat); - if ((mOutputStart != nullptr) && (mOutputEnd != nullptr) && (mOutputStart < mOutputEnd)) + if (mOutputCallback != nullptr) { - mOutputStart += vsnprintf(mOutputStart, static_cast(mOutputEnd - mOutputStart), aFormat, args); + mOutputCallback(aFormat, args, mOutputContext); } va_end(args); @@ -175,17 +737,56 @@ const char *RcpCapsDiag::CategoryToString(Category aCategory) "Basic", // (0) kCategoryBasic "Thread Version >= 1.1", // (1) kCategoryThread1_1 "Thread Version >= 1.2", // (2) kCategoryThread1_2 - "Optional", // (3) kCategoryOptional + "Utils", // (3) kCategoryUtils }; static_assert(kCategoryBasic == 0, "kCategoryBasic value is incorrect"); static_assert(kCategoryThread1_1 == 1, "kCategoryThread1_1 value is incorrect"); static_assert(kCategoryThread1_2 == 2, "kCategoryThread1_2 value is incorrect"); - static_assert(kCategoryOptional == 3, "kCategoryOptional value is incorrect"); + static_assert(kCategoryUtils == 3, "kCategoryUtils value is incorrect"); return (aCategory < OT_ARRAY_LENGTH(kCategoryStrings)) ? kCategoryStrings[aCategory] : "invalid"; } +const char *RcpCapsDiag::SupportToString(bool aSupport) { return aSupport ? "OK" : "NotSupported"; } + +const char *RcpCapsDiag::RadioCapbilityToString(uint32_t aCapability) +{ + static const char *const kCapbilityStrings[] = { + "RADIO_CAPS_ACK_TIMEOUT", // (1 << 0) OT_RADIO_CAPS_ACK_TIMEOUT + "RADIO_CAPS_ENERGY_SCAN", // (1 << 1) OT_RADIO_CAPS_ENERGY_SCAN + "RADIO_CAPS_TRANSMIT_RETRIES", // (1 << 2) OT_RADIO_CAPS_TRANSMIT_RETRIES + "RADIO_CAPS_CSMA_BACKOFF", // (1 << 3) OT_RADIO_CAPS_CSMA_BACKOFF + "RADIO_CAPS_SLEEP_TO_TX", // (1 << 4) OT_RADIO_CAPS_SLEEP_TO_TX + "RADIO_CAPS_TRANSMIT_SEC", // (1 << 5) OT_RADIO_CAPS_TRANSMIT_SEC + "RADIO_CAPS_TRANSMIT_TIMING", // (1 << 6) OT_RADIO_CAPS_TRANSMIT_TIMING + "RADIO_CAPS_RECEIVE_TIMING", // (1 << 7) OT_RADIO_CAPS_RECEIVE_TIMING + "RADIO_CAPS_RX_ON_WHEN_IDLE", // (1 << 8) OT_RADIO_CAPS_RX_ON_WHEN_IDLE + }; + const char *string = "invalid"; + uint16_t index = 0; + + static_assert(OT_RADIO_CAPS_ACK_TIMEOUT == 1 << 0, "OT_RADIO_CAPS_ACK_TIMEOUT value is incorrect"); + static_assert(OT_RADIO_CAPS_ENERGY_SCAN == 1 << 1, "OT_RADIO_CAPS_ENERGY_SCAN value is incorrect"); + static_assert(OT_RADIO_CAPS_TRANSMIT_RETRIES == 1 << 2, "OT_RADIO_CAPS_TRANSMIT_RETRIES value is incorrect"); + static_assert(OT_RADIO_CAPS_CSMA_BACKOFF == 1 << 3, "OT_RADIO_CAPS_CSMA_BACKOFF value is incorrect"); + static_assert(OT_RADIO_CAPS_SLEEP_TO_TX == 1 << 4, "OT_RADIO_CAPS_SLEEP_TO_TX value is incorrect"); + static_assert(OT_RADIO_CAPS_TRANSMIT_SEC == 1 << 5, "OT_RADIO_CAPS_TRANSMIT_SEC value is incorrect"); + static_assert(OT_RADIO_CAPS_TRANSMIT_TIMING == 1 << 6, "OT_RADIO_CAPS_TRANSMIT_TIMING value is incorrect"); + static_assert(OT_RADIO_CAPS_RECEIVE_TIMING == 1 << 7, "OT_RADIO_CAPS_RECEIVE_TIMING value is incorrect"); + static_assert(OT_RADIO_CAPS_RX_ON_WHEN_IDLE == 1 << 8, "OT_RADIO_CAPS_RX_ON_WHEN_IDLE value is incorrect"); + + for (; !(aCapability & 0x1); (aCapability >>= 1), index++) + { + VerifyOrExit(index < OT_ARRAY_LENGTH(kCapbilityStrings)); + } + + string = kCapbilityStrings[index]; + +exit: + return string; +} + } // namespace Posix } // namespace ot #endif // OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE diff --git a/src/posix/platform/rcp_caps_diag.hpp b/src/posix/platform/rcp_caps_diag.hpp index 401c0e72f..0431f6167 100644 --- a/src/posix/platform/rcp_caps_diag.hpp +++ b/src/posix/platform/rcp_caps_diag.hpp @@ -37,6 +37,8 @@ #include "platform-posix.h" #if OPENTHREAD_POSIX_CONFIG_RCP_CAPS_DIAG_ENABLE +#include + #include "lib/spinel/radio_spinel.hpp" #include "lib/spinel/spinel.h" @@ -58,8 +60,8 @@ class RcpCapsDiag */ explicit RcpCapsDiag(Spinel::RadioSpinel &aRadioSpinel) : mRadioSpinel(aRadioSpinel) - , mOutputStart(nullptr) - , mOutputEnd(nullptr) + , mOutputCallback(nullptr) + , mOutputContext(nullptr) { } @@ -68,15 +70,22 @@ class RcpCapsDiag * * @param[in] aArgs The arguments of diagnostics command line. * @param[in] aArgsLength The number of arguments in @p aArgs. - * @param[out] aOutput The diagnostics execution result. - * @param[in] aOutputMaxLen The output buffer size. * * @retval OT_ERROR_INVALID_ARGS The command is supported but invalid arguments provided. * @retval OT_ERROR_NONE The command is successfully processed. * @retval OT_ERROR_INVALID_COMMAND The command is not valid or not supported. * */ - otError DiagProcess(char *aArgs[], uint8_t aArgsLength, char *aOutput, size_t aOutputMaxLen); + otError DiagProcess(char *aArgs[], uint8_t aArgsLength); + + /** + * Sets the diag output callback. + * + * @param[in] aCallback A pointer to a function that is called on outputting diag messages. + * @param[in] aContext A user context pointer. + * + */ + void SetDiagOutputCallback(otPlatDiagOutputCallback aCallback, void *aContext); private: template otError HandleSpinelCommand(void); @@ -87,7 +96,7 @@ class RcpCapsDiag kCategoryBasic, kCategoryThread1_1, kCategoryThread1_2, - kCategoryOptional, + kCategoryUtils, kNumCategories, }; @@ -99,18 +108,37 @@ class RcpCapsDiag RcpCapsDiag::SpinelCommandHandler mHandler; }; + static constexpr uint16_t kMaxNumChildren = 512; + void ProcessSpinel(void); + void ProcessCapabilityFlags(void); + void ProcessSrcMatchTable(void); void TestSpinelCommands(Category aCategory); + void TestRadioCapbilityFlags(void); + void OutputRadioCapFlags(Category aCategory, uint32_t aRadioCaps, const uint32_t *aFlags, uint16_t aNumbFlags); + void TestSpinelCapbilityFlags(void); + void OutputSpinelCapFlags(Category aCategory, + const uint8_t *aCapsData, + spinel_size_t aCapsLength, + const uint32_t *aFlags, + uint16_t aNumbFlags); + bool IsSpinelCapabilitySupported(const uint8_t *aCapsData, spinel_size_t aCapsLength, uint32_t aCapability); + void OutputExtendedSrcMatchTableSize(void); + void OutputShortSrcMatchTableSize(void); + void OutputFormat(const char *aName, const char *aValue); + void OutputFormat(const char *aName, uint32_t aValue); void OutputResult(const SpinelEntry &aEntry, otError error); void Output(const char *aFormat, ...); - const char *CategoryToString(Category aCategory); + static const char *SupportToString(bool aSupport); + static const char *RadioCapbilityToString(uint32_t aCapability); + static const char *CategoryToString(Category aCategory); static const struct SpinelEntry sSpinelEntries[]; - Spinel::RadioSpinel &mRadioSpinel; - char *mOutputStart; - char *mOutputEnd; + Spinel::RadioSpinel &mRadioSpinel; + otPlatDiagOutputCallback mOutputCallback; + void *mOutputContext; }; } // namespace Posix diff --git a/src/posix/platform/spi_interface.cpp b/src/posix/platform/spi_interface.cpp index f3078b46c..2040cd58e 100644 --- a/src/posix/platform/spi_interface.cpp +++ b/src/posix/platform/spi_interface.cpp @@ -326,7 +326,7 @@ uint8_t *SpiInterface::GetRealRxFrameStart(uint8_t *aSpiRxFrameBuffer, uint8_t a uint8_t *start = aSpiRxFrameBuffer; const uint8_t *end = aSpiRxFrameBuffer + aAlignAllowance; - for (; start != end && start[0] == 0xff; start++) + for (; start != end && ((start[0] == 0xff) || (start[0] == 0x00)); start++) ; aSkipLength = static_cast(start - aSpiRxFrameBuffer); @@ -469,7 +469,7 @@ otError SpiInterface::PushPullSpi(void) DieNow(OT_EXIT_FAILURE); } - // Account for misalignment (0xFF bytes at the start) + // Account for misalignment (0xFF or 0x00 bytes at the start) spiRxFrame = GetRealRxFrameStart(spiRxFrameBuffer, mSpiAlignAllowance, skipAlignAllowanceLength); { diff --git a/src/posix/platform/system.cpp b/src/posix/platform/system.cpp index 8de729c4a..89da6af13 100644 --- a/src/posix/platform/system.cpp +++ b/src/posix/platform/system.cpp @@ -57,6 +57,7 @@ #include "posix/platform/mainloop.hpp" #include "posix/platform/mdns_socket.hpp" #include "posix/platform/radio_url.hpp" +#include "posix/platform/spinel_driver_getter.hpp" #include "posix/platform/udp.hpp" otInstance *gInstance = nullptr; @@ -258,6 +259,8 @@ CoprocessorType otSysInitCoprocessor(otPlatformCoprocessorUrls *aUrls) return sCoprocessorType; } +otSpinelDriver *otSysGetSpinelDriver(void) { return &ot::Posix::GetSpinelDriver(); } + otInstance *otSysInit(otPlatformConfig *aPlatformConfig) { OT_ASSERT(gInstance == nullptr); diff --git a/tests/fuzz/fuzzer_platform.cpp b/tests/fuzz/fuzzer_platform.cpp index abc8baae3..524760954 100644 --- a/tests/fuzz/fuzzer_platform.cpp +++ b/tests/fuzz/fuzzer_platform.cpp @@ -488,17 +488,18 @@ otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex) void otPlatSettingsWipe(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); } -otError otPlatDiagProcess(otInstance *aInstance, - uint8_t aArgsLength, - char *aArgs[], - char *aOutput, - size_t aOutputMaxLen) +void otPlatDiagSetOutputCallback(otInstance *aInstance, otPlatDiagOutputCallback aCallback, void *aContext) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aCallback); + OT_UNUSED_VARIABLE(aContext); +} + +otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aInstance); OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); - OT_UNUSED_VARIABLE(aOutput); - OT_UNUSED_VARIABLE(aOutputMaxLen); return OT_ERROR_INVALID_COMMAND; } diff --git a/tests/scripts/expect/cli-diags.exp b/tests/scripts/expect/cli-diags.exp index e5d2fbfab..b9ab58071 100755 --- a/tests/scripts/expect/cli-diags.exp +++ b/tests/scripts/expect/cli-diags.exp @@ -126,6 +126,35 @@ expect "start diagnostics mode" expect "status 0x00" expect_line "Done" +send_user "input too short test\n" +send "diag frame 11\n" +expect "Error" + +send_user "input too long test\n" +send "diag frame 11223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900\n" +expect "Error" + +send_user "input odd length test\n" +send "diag frame 123\n" +expect "Error" + +send_user "shortest frame test\n" +send "diag frame 112233\n" +expect "Done" +send "diag send 1\n" +expect "length 0x3" +expect "Done" + +send_user "longest frame test\n" +send "diag frame 11223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677\n" +expect "Done" +send "diag repeat 1\n" +expect "length 0x7f" +expect "Done" + +send "diag repeat stop\n" +expect "Done" + send "diag channel 11\n" expect "set channel to 11" expect "status 0x00" diff --git a/tests/scripts/expect/cli-ipmaddr.exp b/tests/scripts/expect/cli-ipmaddr.exp index 0c9babd6d..5ab10f986 100755 --- a/tests/scripts/expect/cli-ipmaddr.exp +++ b/tests/scripts/expect/cli-ipmaddr.exp @@ -59,16 +59,6 @@ send "ipmaddr del ff0e::1\n" expect_line "Done" send "ipmaddr del ff0e::1\n" expect "Error 23: NotFound" -send "ipmaddr promiscuous enable\n" -expect_line "Done" -send "ipmaddr promiscuous\n" -expect "Enabled" -expect_line "Done" -send "ipmaddr promiscuous disable\n" -expect_line "Done" -send "ipmaddr promiscuous\n" -expect "Disabled" -expect_line "Done" send "ipmaddr something_invalid\n" expect "Error 35: InvalidCommand" diff --git a/tests/scripts/thread-cert/Cert_5_8_02_KeyIncrement.py b/tests/scripts/thread-cert/Cert_5_8_02_KeyIncrement.py index af305c613..230b3163d 100755 --- a/tests/scripts/thread-cert/Cert_5_8_02_KeyIncrement.py +++ b/tests/scripts/thread-cert/Cert_5_8_02_KeyIncrement.py @@ -42,13 +42,11 @@ class Cert_5_8_2_KeyIncrement(thread_cert.TestCase): TOPOLOGY = { LEADER: { 'name': 'LEADER', - 'key_switch_guardtime': 0, 'mode': 'rdn', 'allowlist': [ROUTER] }, ROUTER: { 'name': 'ROUTER', - 'key_switch_guardtime': 0, 'mode': 'rdn', 'allowlist': [LEADER] }, diff --git a/tests/scripts/thread-cert/Cert_5_8_03_KeyIncrementRollOver.py b/tests/scripts/thread-cert/Cert_5_8_03_KeyIncrementRollOver.py index 382c247aa..68a2bd7a4 100755 --- a/tests/scripts/thread-cert/Cert_5_8_03_KeyIncrementRollOver.py +++ b/tests/scripts/thread-cert/Cert_5_8_03_KeyIncrementRollOver.py @@ -43,13 +43,11 @@ class Cert_5_8_3_KeyIncrementRollOver(thread_cert.TestCase): LEADER: { 'name': 'LEADER', 'key_sequence_counter': 127, - 'key_switch_guardtime': 0, 'mode': 'rdn', 'allowlist': [ROUTER] }, ROUTER: { 'name': 'ROUTER', - 'key_switch_guardtime': 0, 'mode': 'rdn', 'allowlist': [LEADER] }, diff --git a/tests/scripts/thread-cert/Cert_6_6_01_KeyIncrement.py b/tests/scripts/thread-cert/Cert_6_6_01_KeyIncrement.py index 6ad6e6ecc..b1a907188 100755 --- a/tests/scripts/thread-cert/Cert_6_6_01_KeyIncrement.py +++ b/tests/scripts/thread-cert/Cert_6_6_01_KeyIncrement.py @@ -42,14 +42,12 @@ class Cert_6_6_1_KeyIncrement(thread_cert.TestCase): TOPOLOGY = { LEADER: { 'name': 'LEADER', - 'key_switch_guardtime': 0, 'mode': 'rdn', 'allowlist': [ED] }, ED: { 'name': 'ED', 'is_mtd': True, - 'key_switch_guardtime': 0, 'mode': 'rn', 'allowlist': [LEADER] }, diff --git a/tests/scripts/thread-cert/Cert_6_6_02_KeyIncrementRollOver.py b/tests/scripts/thread-cert/Cert_6_6_02_KeyIncrementRollOver.py index d7e078537..812e7f3d8 100755 --- a/tests/scripts/thread-cert/Cert_6_6_02_KeyIncrementRollOver.py +++ b/tests/scripts/thread-cert/Cert_6_6_02_KeyIncrementRollOver.py @@ -43,14 +43,12 @@ class Cert_6_6_2_KeyIncrement1(thread_cert.TestCase): LEADER: { 'name': 'LEADER', 'key_sequence_counter': 127, - 'key_switch_guardtime': 0, 'mode': 'rdn', 'allowlist': [ED] }, ED: { 'name': 'ED', 'is_mtd': True, - 'key_switch_guardtime': 0, 'mode': 'rn', 'allowlist': [LEADER] }, diff --git a/tests/scripts/thread-cert/thread_cert.py b/tests/scripts/thread-cert/thread_cert.py index 9603309ba..6da6dc2cc 100644 --- a/tests/scripts/thread-cert/thread_cert.py +++ b/tests/scripts/thread-cert/thread_cert.py @@ -207,8 +207,6 @@ def _setUp(self): channel=params['pending_dataset'].get('channel'), delay=params['pending_dataset'].get('delay')) - if 'key_switch_guardtime' in params: - self.nodes[i].set_key_switch_guardtime(params['key_switch_guardtime']) if 'key_sequence_counter' in params: self.nodes[i].set_key_sequence_counter(params['key_sequence_counter']) diff --git a/tests/toranj/cli/test-030-anycast-forwarding.py b/tests/toranj/cli/test-030-anycast-forwarding.py new file mode 100755 index 000000000..d4b1c5c3d --- /dev/null +++ b/tests/toranj/cli/test-030-anycast-forwarding.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2024, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: ALOC address forwarding to ED and SED devices. +# +# Network topology +# +# r1 ---- r2 ---- r3 +# /|\ +# / | \ +# ed sed sed2 +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 40 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +r3 = cli.Node() +ed = cli.Node() +sed = cli.Node() +sed2 = cli.Node() + +nodes = [r1, r2, r3, ed, sed, sed2] + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) + +r2.allowlist_node(r1) +r2.allowlist_node(r3) + +r3.allowlist_node(r2) +r3.allowlist_node(ed) +r3.allowlist_node(sed) +r3.allowlist_node(sed2) + +ed.allowlist_node(r3) +sed.allowlist_node(r3) +sed2.allowlist_node(r3) + +r1.form('aloc') +r2.join(r1) +r3.join(r2) +ed.join(r2, cli.JOIN_TYPE_END_DEVICE) +sed.join(r3, cli.JOIN_TYPE_SLEEPY_END_DEVICE) +sed2.join(r3, cli.JOIN_TYPE_SLEEPY_END_DEVICE) + +sed.set_pollperiod(400) +sed2.set_pollperiod(500) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') +verify(r3.get_state() == 'router') +verify(ed.get_state() == 'child') +verify(sed.get_state() == 'child') +verify(sed.get_state() == 'child') + +sed.set_mode('n') + +for child in [ed, sed, sed2]: + verify(int(child.get_parent_info()['Rloc'], 16) == int(r3.get_rloc16(), 16)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + + +def check_netdata_services(expected_num_services): + # Check that all nodes see the `expected_num_services` service + # entries in network data. + for node in nodes: + verify(len(node.get_netdata_services()) == expected_num_services) + + +wait_time = 5 + +# Add a service on ed which is non-sleepy child. + +ed.cli('service add 44970 11 00') +ed.register_netdata() + +verify_within(check_netdata_services, wait_time, 1) + +# Verify that the ALOC associated with a non-sleepy child's service is +# pingable from all nodes. + +aloc1 = r1.get_mesh_local_prefix().split('/')[0] + 'ff:fe00:fc10' + +for node in nodes: + node.ping(aloc1) + +# Add a service now from `sed` which is a sleepy child and again make sure +# its ALOC can be pinged from all other nodes. + +sed.cli('service add 44970 22 00') +sed.register_netdata() + +verify_within(check_netdata_services, wait_time, 2) + +aloc2 = r1.get_mesh_local_prefix().split('/')[0] + 'ff:fe00:fc11' + +r1.ping(aloc2) + +for node in nodes: + node.ping(aloc2) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/openthread-core-toranj-config-simulation.h b/tests/toranj/openthread-core-toranj-config-simulation.h index e0f030e57..88b958615 100644 --- a/tests/toranj/openthread-core-toranj-config-simulation.h +++ b/tests/toranj/openthread-core-toranj-config-simulation.h @@ -76,6 +76,8 @@ #define OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#define OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + #define OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE 1 #define OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE 1 diff --git a/tests/toranj/start.sh b/tests/toranj/start.sh index ebbbdd700..ef471b00e 100755 --- a/tests/toranj/start.sh +++ b/tests/toranj/start.sh @@ -194,6 +194,7 @@ if [ "$TORANJ_CLI" = 1 ]; then run cli/test-027-slaac-address.py run cli/test-028-border-agent-ephemeral-key.py run cli/test-029-pending-dataset-key-change.py + run cli/test-030-anycast-forwarding.py run cli/test-400-srp-client-server.py run cli/test-401-srp-server-address-cache-snoop.py run cli/test-500-two-brs-two-networks.py diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index a8364f7a7..92aad7622 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -176,6 +176,7 @@ ot_unit_test(data) ot_unit_test(dataset) ot_unit_test(dns) ot_unit_test(dns_client) +ot_unit_test(dnssd_discovery_proxy) ot_unit_test(dso) ot_unit_test(ecdsa) ot_unit_test(flash) diff --git a/tests/unit/test_child.cpp b/tests/unit/test_child.cpp index eb239f085..560fc9a9f 100644 --- a/tests/unit/test_child.cpp +++ b/tests/unit/test_child.cpp @@ -57,7 +57,7 @@ void VerifyChildIp6Addresses(const Child &aChild, uint8_t aAddressListLength, co memset(addressObserved, 0, sizeof(addressObserved)); - for (const Ip6::Address &address : aChild.IterateIp6Addresses()) + for (const Ip6::Address &address : aChild.GetIp6Addresses()) { bool addressIsInList = false; @@ -78,14 +78,16 @@ void VerifyChildIp6Addresses(const Child &aChild, uint8_t aAddressListLength, co { Ip6::Address address; - VerifyOrQuit(addressObserved[index], "Child::IterateIp6Addresses() missed an entry from the expected list"); - if (sInstance->Get().IsMeshLocalAddress(aAddressList[index])) { SuccessOrQuit(aChild.GetMeshLocalIp6Address(address)); VerifyOrQuit(address == aAddressList[index], "GetMeshLocalIp6Address() did not return expected address"); hasMeshLocal = true; } + else + { + VerifyOrQuit(addressObserved[index], "Child::IterateIp6Addresses() missed an entry from the expected list"); + } } if (!hasMeshLocal) @@ -95,95 +97,6 @@ void VerifyChildIp6Addresses(const Child &aChild, uint8_t aAddressListLength, co VerifyOrQuit(aChild.GetMeshLocalIp6Address(address) == kErrorNotFound, "Child::GetMeshLocalIp6Address() returned an address not in the expected list"); } - - // Iterate over unicast and multicast addresses separately. - - memset(addressObserved, 0, sizeof(addressObserved)); - - for (Ip6::Address::TypeFilter filter : filters) - { - for (const Ip6::Address &address : aChild.IterateIp6Addresses(filter)) - { - bool addressIsInList = false; - - switch (filter) - { - case Ip6::Address::kTypeMulticast: - VerifyOrQuit(address.IsMulticast(), "Address::TypeFilter failed"); - break; - - case Ip6::Address::kTypeUnicast: - VerifyOrQuit(!address.IsMulticast(), "Address::TypeFilter failed"); - break; - - default: - break; - } - - VerifyOrQuit(address.MatchesFilter(filter)); - - for (uint8_t index = 0; index < aAddressListLength; index++) - { - if (address == aAddressList[index]) - { - VerifyOrQuit(addressObserved[index] == false, - "Child::IterateIp6Addresses() returned duplicate addr"); - addressObserved[index] = true; - addressIsInList = true; - break; - } - } - - VerifyOrQuit(addressIsInList, "Child::IterateIp6Addresses() returned an address not in the expected list"); - } - } - - for (uint8_t index = 0; index < aAddressListLength; index++) - { - VerifyOrQuit(addressObserved[index], "Child::IterateIp6Addresses() missed an entry from the expected list"); - } - - // Verify behavior of `Child::AddressIterator - { - Child::AddressIterator iter1(aChild); - Child::AddressIterator iter2(aChild); - Child::AddressIterator::Index iterIndex; - - for (const Ip6::Address &address : aChild.IterateIp6Addresses()) - { - VerifyOrQuit(iter1 == iter2); - VerifyOrQuit(!iter1.IsDone()); - VerifyOrQuit(*iter1.GetAddress() == address); - VerifyOrQuit(*iter1.GetAddress() == *iter2.GetAddress()); - - iterIndex = iter1.GetAsIndex(); - VerifyOrQuit(iter2.GetAsIndex() == iterIndex); - - { - Child::AddressIterator iter3(aChild, iterIndex); - VerifyOrQuit(iter3 == iter1, "AddressIterator(iterIndex) failed"); - - iter3++; - VerifyOrQuit(iter3 != iter1, "AddressIterator(iterIndex) failed"); - } - - iter1++; - VerifyOrQuit(iter1 != iter2); - iter2++; - } - - VerifyOrQuit(iter1.IsDone()); - VerifyOrQuit(iter2.IsDone()); - VerifyOrQuit(iter1 == iter2); - - iterIndex = iter1.GetAsIndex(); - VerifyOrQuit(iter2.GetAsIndex() == iterIndex); - - { - Child::AddressIterator iter3(aChild, iterIndex); - VerifyOrQuit(iter3 == iter1, "AddressIterator(iterIndex) failed"); - } - } } void TestChildIp6Address(void) @@ -214,7 +127,7 @@ void TestChildIp6Address(void) numAddresses = 0; // First addresses uses the mesh local prefix (mesh-local address). - addresses[numAddresses] = sInstance->Get().GetMeshLocal64(); + addresses[numAddresses] = sInstance->Get().GetMeshLocalEid(); addresses[numAddresses].SetIid(meshLocalIid); numAddresses++; diff --git a/tests/unit/test_dns_client.cpp b/tests/unit/test_dns_client.cpp index 176654c0f..e91376689 100644 --- a/tests/unit/test_dns_client.cpp +++ b/tests/unit/test_dns_client.cpp @@ -671,8 +671,8 @@ void TestDnsClient(void) Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); - Log("Set TestMode on server to only accept single question"); - dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeSingleQuestionOnly); + Log("Set TestMode on server to reject multi-question queries and send error"); + dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeRejectMultiQuestionQuery); Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName, ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize)); @@ -706,6 +706,48 @@ void TestDnsClient(void) dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeDisabled); + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + Log("Set TestMode on server to ignore multi-question queries (send no response)"); + dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeIgnoreMultiQuestionQuery); + + Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName, + ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize)); + + queryConfig.Clear(); + queryConfig.mServiceMode = static_cast(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize); + + sResolveServiceInfo.Reset(); + SuccessOrQuit( + dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig)); + + AdvanceTime(10 * 1000); // Wait longer than client response timeout. + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + SuccessOrQuit(sResolveServiceInfo.mError); + + // Use `kServiceModeSrvTxt` and check that server does ignore two questions. + + Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName, + ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrvTxt)); + + queryConfig.Clear(); + queryConfig.mServiceMode = static_cast(Dns::Client::QueryConfig::kServiceModeSrvTxt); + + sResolveServiceInfo.Reset(); + SuccessOrQuit( + dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig)); + + // Wait for the client to time out after exhausting all retry attempts, and + // ensure that a `kErrorResponseTimeout` error is reported. + + AdvanceTime(45 * 1000); + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + VerifyOrQuit(sResolveServiceInfo.mError == kErrorResponseTimeout); + + dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeDisabled); + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Validate DNS Client `ResolveService()` using all service modes // when sever does not provide any RR in the addition data section. @@ -832,7 +874,7 @@ void TestDnsClient(void) Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); Log("Set TestMode on server to not include any RR in additional section AND to only accept single question"); dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeEmptyAdditionalSection + - Dns::ServiceDiscovery::Server::kTestModeSingleQuestionOnly); + Dns::ServiceDiscovery::Server::kTestModeRejectMultiQuestionQuery); Log("ResolveServiceAndHostAddress(%s,%s) with ServiceMode: %s", kInstance1Label, kService1FullName, ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize)); diff --git a/tests/unit/test_dnssd_discovery_proxy.cpp b/tests/unit/test_dnssd_discovery_proxy.cpp new file mode 100644 index 000000000..c2dfc0d04 --- /dev/null +++ b/tests/unit/test_dnssd_discovery_proxy.cpp @@ -0,0 +1,2723 @@ +/* + * Copyright (c) 2023, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "test_platform.h" +#include "test_util.hpp" + +#include +#include +#include +#include +#include + +#include "common/arg_macros.hpp" +#include "common/array.hpp" +#include "common/clearable.hpp" +#include "common/string.hpp" +#include "common/time.hpp" +#include "instance/instance.hpp" + +#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE && \ + OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE && OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE && \ + OPENTHREAD_CONFIG_DNSSD_DISCOVERY_PROXY_ENABLE && OPENTHREAD_CONFIG_SRP_SERVER_ENABLE && \ + OPENTHREAD_CONFIG_PLATFORM_DNSSD_ALLOW_RUN_TIME_SELECTION && OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE && \ + !OPENTHREAD_CONFIG_TIME_SYNC_ENABLE && !OPENTHREAD_PLATFORM_POSIX +#define ENABLE_DISCOVERY_PROXY_TEST 1 +#else +#define ENABLE_DISCOVERY_PROXY_TEST 0 +#endif + +#if ENABLE_DISCOVERY_PROXY_TEST + +using namespace ot; + +// Logs a message and adds current time (sNow) as "::." +#define Log(...) \ + printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 36000000), (sNow / 60000) % 60, \ + (sNow / 1000) % 60, sNow % 1000 OT_REST_ARGS(__VA_ARGS__)) + +static constexpr uint16_t kMaxRaSize = 800; + +static ot::Instance *sInstance; + +static uint32_t sNow = 0; +static uint32_t sAlarmTime; +static bool sAlarmOn = false; + +static otRadioFrame sRadioTxFrame; +static uint8_t sRadioTxFramePsdu[OT_RADIO_FRAME_MAX_SIZE]; +static bool sRadioTxOngoing = false; + +//---------------------------------------------------------------------------------------------------------------------- +// Function prototypes + +void ProcessRadioTxAndTasklets(void); +void AdvanceTime(uint32_t aDuration); + +//---------------------------------------------------------------------------------------------------------------------- +// `otPlatRadio` + +extern "C" { + +otRadioCaps otPlatRadioGetCaps(otInstance *) { return OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_CSMA_BACKOFF; } + +otError otPlatRadioTransmit(otInstance *, otRadioFrame *) +{ + sRadioTxOngoing = true; + + return OT_ERROR_NONE; +} + +otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *) { return &sRadioTxFrame; } + +//---------------------------------------------------------------------------------------------------------------------- +// `otPlatAlaram` + +void otPlatAlarmMilliStop(otInstance *) { sAlarmOn = false; } + +void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt) +{ + sAlarmOn = true; + sAlarmTime = aT0 + aDt; +} + +uint32_t otPlatAlarmMilliGetNow(void) { return sNow; } + +//---------------------------------------------------------------------------------------------------------------------- + +Array sHeapAllocatedPtrs; + +#if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE +void *otPlatCAlloc(size_t aNum, size_t aSize) +{ + void *ptr = calloc(aNum, aSize); + + SuccessOrQuit(sHeapAllocatedPtrs.PushBack(ptr)); + + return ptr; +} + +void otPlatFree(void *aPtr) +{ + if (aPtr != nullptr) + { + void **entry = sHeapAllocatedPtrs.Find(aPtr); + + VerifyOrQuit(entry != nullptr, "A heap allocated item is freed twice"); + sHeapAllocatedPtrs.Remove(*entry); + } + + free(aPtr); +} +#endif + +#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED +void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) +{ + OT_UNUSED_VARIABLE(aLogLevel); + OT_UNUSED_VARIABLE(aLogRegion); + + va_list args; + + printf(" "); + va_start(args, aFormat); + vprintf(aFormat, args); + va_end(args); + printf("\n"); +} +#endif + +} // extern "C" + +//--------------------------------------------------------------------------------------------------------------------- + +void ProcessRadioTxAndTasklets(void) +{ + do + { + if (sRadioTxOngoing) + { + sRadioTxOngoing = false; + otPlatRadioTxStarted(sInstance, &sRadioTxFrame); + otPlatRadioTxDone(sInstance, &sRadioTxFrame, nullptr, OT_ERROR_NONE); + } + + otTaskletsProcess(sInstance); + } while (otTaskletsArePending(sInstance)); +} + +void AdvanceTime(uint32_t aDuration) +{ + uint32_t time = sNow + aDuration; + + Log("AdvanceTime for %u.%03u", aDuration / 1000, aDuration % 1000); + + while (TimeMilli(sAlarmTime) <= TimeMilli(time)) + { + ProcessRadioTxAndTasklets(); + sNow = sAlarmTime; + otPlatAlarmMilliFired(sInstance); + } + + ProcessRadioTxAndTasklets(); + sNow = time; +} + +void InitTest(void) +{ + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Initialize OT instance. + + sNow = 0; + sAlarmOn = false; + sInstance = static_cast(testInitInstance()); + + memset(&sRadioTxFrame, 0, sizeof(sRadioTxFrame)); + sRadioTxFrame.mPsdu = sRadioTxFramePsdu; + sRadioTxOngoing = false; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Initialize Border Router and start Thread operation. + + otOperationalDataset dataset; + otOperationalDatasetTlvs datasetTlvs; + + SuccessOrQuit(otDatasetCreateNewNetwork(sInstance, &dataset)); + otDatasetConvertToTlvs(&dataset, &datasetTlvs); + SuccessOrQuit(otDatasetSetActiveTlvs(sInstance, &datasetTlvs)); + + SuccessOrQuit(otIp6SetEnabled(sInstance, true)); + SuccessOrQuit(otThreadSetEnabled(sInstance, true)); + + // Configure the `Dnssd` module to use `otPlatDnssd` APIs. + + sInstance->Get().SetUseNativeMdns(false); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Ensure device starts as leader. + + AdvanceTime(10000); + + VerifyOrQuit(otThreadGetDeviceRole(sInstance) == OT_DEVICE_ROLE_LEADER); +} + +void FinalizeTest(void) +{ + SuccessOrQuit(otIp6SetEnabled(sInstance, false)); + SuccessOrQuit(otThreadSetEnabled(sInstance, false)); + // Make sure there is no message/buffer leak + VerifyOrQuit(sInstance->Get().GetFreeBufferCount() == + sInstance->Get().GetTotalBufferCount()); + SuccessOrQuit(otInstanceErasePersistentInfo(sInstance)); + testFreeInstance(sInstance); +} + +//--------------------------------------------------------------------------------------------------------------------- +// DNS Client callback + +void LogServiceInfo(const Dns::Client::ServiceInfo &aInfo) +{ + Log(" TTL: %u", aInfo.mTtl); + Log(" Port: %u", aInfo.mPort); + Log(" Weight: %u", aInfo.mWeight); + Log(" HostName: %s", aInfo.mHostNameBuffer); + Log(" HostAddr: %s", AsCoreType(&aInfo.mHostAddress).ToString().AsCString()); + Log(" TxtDataLength: %u", aInfo.mTxtDataSize); + Log(" TxtDataTTL: %u", aInfo.mTxtDataTtl); +} + +const char *ServiceModeToString(Dns::Client::QueryConfig::ServiceMode aMode) +{ + static const char *const kServiceModeStrings[] = { + "unspec", // kServiceModeUnspecified (0) + "srv", // kServiceModeSrv (1) + "txt", // kServiceModeTxt (2) + "srv_txt", // kServiceModeSrvTxt (3) + "srv_txt_sep", // kServiceModeSrvTxtSeparate (4) + "srv_txt_opt", // kServiceModeSrvTxtOptimize (5) + }; + + static_assert(Dns::Client::QueryConfig::kServiceModeUnspecified == 0, "Unspecified value is incorrect"); + static_assert(Dns::Client::QueryConfig::kServiceModeSrv == 1, "Srv value is incorrect"); + static_assert(Dns::Client::QueryConfig::kServiceModeTxt == 2, "Txt value is incorrect"); + static_assert(Dns::Client::QueryConfig::kServiceModeSrvTxt == 3, "SrvTxt value is incorrect"); + static_assert(Dns::Client::QueryConfig::kServiceModeSrvTxtSeparate == 4, "SrvTxtSeparate value is incorrect"); + static_assert(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize == 5, "SrvTxtOptimize value is incorrect"); + + return kServiceModeStrings[aMode]; +} + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +struct BrowseInfo +{ + void Reset(void) { mCallbackCount = 0; } + + uint16_t mCallbackCount; + Error mError; + char mServiceName[Dns::Name::kMaxNameSize]; + char mInstanceLabel[Dns::Name::kMaxLabelSize]; + uint16_t mNumInstances; + Dns::Client::ServiceInfo mServiceInfo; + char mHostNameBuffer[Dns::Name::kMaxNameSize]; + uint8_t mTxtBuffer[255]; +}; + +static BrowseInfo sBrowseInfo; + +void BrowseCallback(otError aError, const otDnsBrowseResponse *aResponse, void *aContext) +{ + const Dns::Client::BrowseResponse &response = AsCoreType(aResponse); + + Log("BrowseCallback"); + Log(" Error: %s", ErrorToString(aError)); + + VerifyOrQuit(aContext == sInstance); + + sBrowseInfo.mCallbackCount++; + sBrowseInfo.mError = aError; + + SuccessOrExit(aError); + + SuccessOrQuit(response.GetServiceName(sBrowseInfo.mServiceName, sizeof(sBrowseInfo.mServiceName))); + Log(" ServiceName: %s", sBrowseInfo.mServiceName); + + for (uint16_t index = 0;; index++) + { + Error error; + + error = response.GetServiceInstance(index, sBrowseInfo.mInstanceLabel, sizeof(sBrowseInfo.mInstanceLabel)); + + if (error == kErrorNotFound) + { + sBrowseInfo.mNumInstances = index; + break; + } + + SuccessOrQuit(error); + + Log(" %2u) %s", index + 1, sBrowseInfo.mInstanceLabel); + } + + if (sBrowseInfo.mNumInstances == 1) + { + sBrowseInfo.mServiceInfo.mHostNameBuffer = sBrowseInfo.mHostNameBuffer; + sBrowseInfo.mServiceInfo.mHostNameBufferSize = sizeof(sBrowseInfo.mHostNameBuffer); + sBrowseInfo.mServiceInfo.mTxtData = sBrowseInfo.mTxtBuffer; + sBrowseInfo.mServiceInfo.mTxtDataSize = sizeof(sBrowseInfo.mTxtBuffer); + + SuccessOrExit(response.GetServiceInfo(sBrowseInfo.mInstanceLabel, sBrowseInfo.mServiceInfo)); + LogServiceInfo(sBrowseInfo.mServiceInfo); + } + +exit: + return; +} + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +static constexpr uint8_t kMaxHostAddresses = 10; +static constexpr uint16_t kMaxTxtBuffer = 256; + +struct ResolveServiceInfo +{ + void Reset(void) + { + memset(this, 0, sizeof(*this)); + mInfo.mHostNameBuffer = mNameBuffer; + mInfo.mHostNameBufferSize = sizeof(mNameBuffer); + mInfo.mTxtData = mTxtBuffer; + mInfo.mTxtDataSize = sizeof(mTxtBuffer); + }; + + uint16_t mCallbackCount; + Error mError; + Dns::Client::ServiceInfo mInfo; + char mNameBuffer[Dns::Name::kMaxNameSize]; + uint8_t mTxtBuffer[kMaxTxtBuffer]; + Ip6::Address mHostAddresses[kMaxHostAddresses]; + uint8_t mNumHostAddresses; +}; + +static ResolveServiceInfo sResolveServiceInfo; + +void ServiceCallback(otError aError, const otDnsServiceResponse *aResponse, void *aContext) +{ + const Dns::Client::ServiceResponse &response = AsCoreType(aResponse); + char instLabel[Dns::Name::kMaxLabelSize]; + char serviceName[Dns::Name::kMaxNameSize]; + + Log("ServiceCallback"); + Log(" Error: %s", ErrorToString(aError)); + + VerifyOrQuit(aContext == sInstance); + + SuccessOrQuit(response.GetServiceName(instLabel, sizeof(instLabel), serviceName, sizeof(serviceName))); + Log(" InstLabel: %s", instLabel); + Log(" ServiceName: %s", serviceName); + + sResolveServiceInfo.mCallbackCount++; + sResolveServiceInfo.mError = aError; + + SuccessOrExit(aError); + + aError = response.GetServiceInfo(sResolveServiceInfo.mInfo); + + if (aError == kErrorNotFound) + { + sResolveServiceInfo.mError = aError; + ExitNow(); + } + + SuccessOrQuit(aError); + + for (uint8_t index = 0; index < kMaxHostAddresses; index++) + { + Error error; + uint32_t ttl; + + error = response.GetHostAddress(sResolveServiceInfo.mInfo.mHostNameBuffer, index, + sResolveServiceInfo.mHostAddresses[index], ttl); + + if (error == kErrorNotFound) + { + sResolveServiceInfo.mNumHostAddresses = index; + break; + } + + SuccessOrQuit(error); + } + + LogServiceInfo(sResolveServiceInfo.mInfo); + Log(" NumHostAddresses: %u", sResolveServiceInfo.mNumHostAddresses); + + for (uint8_t index = 0; index < sResolveServiceInfo.mNumHostAddresses; index++) + { + Log(" %s", sResolveServiceInfo.mHostAddresses[index].ToString().AsCString()); + } + +exit: + return; +} + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +struct ResolveAddressInfo +{ + void Reset(void) { memset(this, 0, sizeof(*this)); }; + + uint16_t mCallbackCount; + Error mError; + char mHostName[Dns::Name::kMaxNameSize]; + Ip6::Address mHostAddresses[kMaxHostAddresses]; + uint8_t mNumHostAddresses; + uint32_t mTtl; +}; + +static ResolveAddressInfo sResolveAddressInfo; + +void AddressCallback(otError aError, const otDnsAddressResponse *aResponse, void *aContext) +{ + const Dns::Client::AddressResponse &response = AsCoreType(aResponse); + + Log("AddressCallback"); + Log(" Error: %s", ErrorToString(aError)); + + VerifyOrQuit(aContext == sInstance); + + sResolveAddressInfo.mCallbackCount++; + sResolveAddressInfo.mError = aError; + + SuccessOrExit(aError); + + SuccessOrQuit(response.GetHostName(sResolveAddressInfo.mHostName, sizeof(sResolveAddressInfo.mHostName))); + Log(" HostName: %s", sResolveAddressInfo.mHostName); + + for (uint8_t index = 0; index < kMaxHostAddresses; index++) + { + Error error; + uint32_t ttl; + + error = response.GetAddress(index, sResolveAddressInfo.mHostAddresses[index], sResolveAddressInfo.mTtl); + + if (error == kErrorNotFound) + { + sResolveAddressInfo.mNumHostAddresses = index; + break; + } + + SuccessOrQuit(error); + } + + Log(" NumHostAddresses: %u", sResolveAddressInfo.mNumHostAddresses); + + for (uint8_t index = 0; index < sResolveAddressInfo.mNumHostAddresses; index++) + { + Log(" %s", sResolveAddressInfo.mHostAddresses[index].ToString().AsCString()); + } + +exit: + return; +} + +//---------------------------------------------------------------------------------------------------------------------- +// otPlatDnssd APIs + +constexpr uint32_t kInfraIfIndex = 1; + +static otPlatDnssdState sDnssdState = OT_PLAT_DNSSD_READY; + +template void CopyString(char (&aStringBuffer)[kSize], const char *aString) +{ + if (aString == nullptr) + { + memset(aStringBuffer, 0, kSize); + } + else + { + uint16_t length = StringLength(aString, kSize); + + VerifyOrQuit(length < kSize); + memcpy(aStringBuffer, aString, length + 1); + } +} + +struct BrowserInfo : public Clearable +{ + bool ServiceTypeMatches(const char *aType) const { return !strcmp(mServiceType, aType); } + bool SubTypeMatches(const char *aSubType) const { return !strcmp(mSubTypeLabel, aSubType); } + + void UpdateFrom(const otPlatDnssdBrowser *aBrowser) + { + mCallCount++; + CopyString(mServiceType, aBrowser->mServiceType); + CopyString(mSubTypeLabel, aBrowser->mSubTypeLabel); + mCallback = aBrowser->mCallback; + } + + uint16_t mCallCount; + char mServiceType[Dns::Name::kMaxNameSize]; + char mSubTypeLabel[Dns::Name::kMaxNameSize]; + otPlatDnssdBrowseCallback mCallback; +}; + +struct SrvResolverInfo : public Clearable +{ + bool ServiceTypeMatches(const char *aType) const { return !strcmp(mServiceType, aType); } + bool ServiceInstanceMatches(const char *aInstance) const { return !strcmp(mServiceInstance, aInstance); } + + void UpdateFrom(const otPlatDnssdSrvResolver *aResolver) + { + mCallCount++; + CopyString(mServiceInstance, aResolver->mServiceInstance); + CopyString(mServiceType, aResolver->mServiceType); + mCallback = aResolver->mCallback; + } + + uint16_t mCallCount; + char mServiceInstance[Dns::Name::kMaxLabelSize]; + char mServiceType[Dns::Name::kMaxNameSize]; + otPlatDnssdSrvCallback mCallback; +}; + +struct TxtResolverInfo : public Clearable +{ + bool ServiceTypeMatches(const char *aType) const { return !strcmp(mServiceType, aType); } + bool ServiceInstanceMatches(const char *aInstance) const { return !strcmp(mServiceInstance, aInstance); } + + void UpdateFrom(const otPlatDnssdTxtResolver *aResolver) + { + mCallCount++; + CopyString(mServiceInstance, aResolver->mServiceInstance); + CopyString(mServiceType, aResolver->mServiceType); + mCallback = aResolver->mCallback; + } + + uint16_t mCallCount; + char mServiceInstance[Dns::Name::kMaxLabelSize]; + char mServiceType[Dns::Name::kMaxNameSize]; + otPlatDnssdTxtCallback mCallback; +}; + +struct Ip6AddrResolverInfo : public Clearable +{ + bool HostNameMatches(const char *aName) const { return !strcmp(mHostName, aName); } + + void UpdateFrom(const otPlatDnssdAddressResolver *aResolver) + { + mCallCount++; + CopyString(mHostName, aResolver->mHostName); + mCallback = aResolver->mCallback; + } + + uint16_t mCallCount; + char mHostName[Dns::Name::kMaxNameSize]; + otPlatDnssdAddressCallback mCallback; +}; + +struct InvokeOnStart : public Clearable +{ + // When not null, these entries are used to invoke callback + // directly from `otPlatDnssdStart{Browser/Resolver}()` APIs. + // This is used in `TestProxyInvokeCallbackFromStartApi()`. + + const otPlatDnssdBrowseResult *mBrowseResult; + const otPlatDnssdSrvResult *mSrvResult; + const otPlatDnssdTxtResult *mTxtResult; + const otPlatDnssdAddressResult *mIp6AddrResult; +}; + +static BrowserInfo sStartBrowserInfo; +static BrowserInfo sStopBrowserInfo; +static SrvResolverInfo sStartSrvResolverInfo; +static SrvResolverInfo sStopSrvResolverInfo; +static TxtResolverInfo sStartTxtResolverInfo; +static TxtResolverInfo sStopTxtResolverInfo; +static Ip6AddrResolverInfo sStartIp6AddrResolverInfo; +static Ip6AddrResolverInfo sStopIp6AddrResolverInfo; + +static InvokeOnStart sInvokeOnStart; + +void ResetPlatDnssdApiInfo(void) +{ + sStartBrowserInfo.Clear(); + sStopBrowserInfo.Clear(); + sStartSrvResolverInfo.Clear(); + sStopSrvResolverInfo.Clear(); + sStartTxtResolverInfo.Clear(); + sStopTxtResolverInfo.Clear(); + sStartIp6AddrResolverInfo.Clear(); + sStopIp6AddrResolverInfo.Clear(); + sInvokeOnStart.Clear(); +} + +const char *StringNullCheck(const char *aString) { return (aString != nullptr) ? aString : "(null)"; } + +void InvokeBrowserCallback(otPlatDnssdBrowseCallback aCallback, const otPlatDnssdBrowseResult &aResult) +{ + Log("Invoking browser callback"); + Log(" serviceType : \"%s\"", StringNullCheck(aResult.mServiceType)); + Log(" subType : \"%s\"", StringNullCheck(aResult.mSubTypeLabel)); + Log(" serviceInstance: \"%s\"", StringNullCheck(aResult.mServiceInstance)); + Log(" ttl : %u", aResult.mTtl); + Log(" if-index : %u", aResult.mInfraIfIndex); + + aCallback(sInstance, &aResult); +} + +void InvokeSrvResolverCallback(otPlatDnssdSrvCallback aCallback, const otPlatDnssdSrvResult &aResult) +{ + Log("Invoking SRV resolver callback"); + Log(" serviceInstance: %s", StringNullCheck(aResult.mServiceInstance)); + Log(" serviceType : %s", StringNullCheck(aResult.mServiceType)); + Log(" hostName : %s", StringNullCheck(aResult.mHostName)); + Log(" port : %u", aResult.mPort); + Log(" priority : %u", aResult.mPriority); + Log(" weight : %u", aResult.mWeight); + Log(" ttl : %u", aResult.mTtl); + Log(" if-index : %u", aResult.mInfraIfIndex); + + aCallback(sInstance, &aResult); +} + +void InvokeTxtResolverCallback(otPlatDnssdTxtCallback aCallback, const otPlatDnssdTxtResult &aResult) +{ + Log("Invoking TXT resolver callback"); + Log(" serviceInstance: %s", StringNullCheck(aResult.mServiceInstance)); + Log(" serviceType : %s", StringNullCheck(aResult.mServiceType)); + Log(" txt-data-len : %u", aResult.mTxtDataLength); + Log(" ttl : %u", aResult.mTtl); + Log(" if-index : %u", aResult.mInfraIfIndex); + + aCallback(sInstance, &aResult); +} + +void InvokeIp6AddrResolverCallback(const otPlatDnssdAddressCallback aCallback, const otPlatDnssdAddressResult &aResult) +{ + Log("Invoking Ip6 resolver callback"); + Log(" hostName : %s", aResult.mHostName); + Log(" if-index : %u", aResult.mInfraIfIndex); + Log(" numAddresses : %u", aResult.mAddressesLength); + for (uint16_t index = 0; index < aResult.mAddressesLength; index++) + { + Log(" address[%u] : %s", index, AsCoreType(&aResult.mAddresses[index].mAddress).ToString().AsCString()); + Log(" ttl[%u] : %u", index, aResult.mAddresses[index].mTtl); + } + + aCallback(sInstance, &aResult); +} + +otPlatDnssdState otPlatDnssdGetState(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + return sDnssdState; +} + +void otPlatDnssdStartBrowser(otInstance *aInstance, const otPlatDnssdBrowser *aBrowser) +{ + VerifyOrQuit(aBrowser != nullptr); + + Log("otPlatDnssdStartBrowser(\"%s\", sub-type:\"%s\")", aBrowser->mServiceType, aBrowser->mSubTypeLabel); + + VerifyOrQuit(aInstance == sInstance); + VerifyOrQuit(aBrowser->mInfraIfIndex == kInfraIfIndex); + + sStartBrowserInfo.UpdateFrom(aBrowser); + + if (sInvokeOnStart.mBrowseResult != nullptr) + { + InvokeBrowserCallback(aBrowser->mCallback, *sInvokeOnStart.mBrowseResult); + } +} + +void otPlatDnssdStopBrowser(otInstance *aInstance, const otPlatDnssdBrowser *aBrowser) +{ + VerifyOrQuit(aBrowser != nullptr); + + Log("otPlatDnssdStopBrowser(\"%s\", sub-type:\"%s\")", aBrowser->mServiceType, aBrowser->mSubTypeLabel); + + VerifyOrQuit(aInstance == sInstance); + VerifyOrQuit(aBrowser->mInfraIfIndex == kInfraIfIndex); + + sStopBrowserInfo.UpdateFrom(aBrowser); +} + +void otPlatDnssdStartSrvResolver(otInstance *aInstance, const otPlatDnssdSrvResolver *aResolver) +{ + VerifyOrQuit(aResolver != nullptr); + + Log("otPlatDnssdStartSrvResolver(\"%s\", \"%s\")", aResolver->mServiceInstance, aResolver->mServiceType); + + VerifyOrQuit(aInstance == sInstance); + VerifyOrQuit(aResolver->mInfraIfIndex == kInfraIfIndex); + + sStartSrvResolverInfo.UpdateFrom(aResolver); + + if (sInvokeOnStart.mSrvResult != nullptr) + { + InvokeSrvResolverCallback(aResolver->mCallback, *sInvokeOnStart.mSrvResult); + } +} + +void otPlatDnssdStopSrvResolver(otInstance *aInstance, const otPlatDnssdSrvResolver *aResolver) +{ + VerifyOrQuit(aResolver != nullptr); + + Log("otPlatDnssdStopSrvResolver(\"%s\", \"%s\")", aResolver->mServiceInstance, aResolver->mServiceType); + + VerifyOrQuit(aInstance == sInstance); + VerifyOrQuit(aResolver->mInfraIfIndex == kInfraIfIndex); + + sStopSrvResolverInfo.UpdateFrom(aResolver); +} + +void otPlatDnssdStartTxtResolver(otInstance *aInstance, const otPlatDnssdTxtResolver *aResolver) +{ + VerifyOrQuit(aResolver != nullptr); + + Log("otPlatDnssdStartTxtResolver(\"%s\", \"%s\")", aResolver->mServiceInstance, aResolver->mServiceType); + + VerifyOrQuit(aInstance == sInstance); + VerifyOrQuit(aResolver->mInfraIfIndex == kInfraIfIndex); + + sStartTxtResolverInfo.UpdateFrom(aResolver); + + if (sInvokeOnStart.mTxtResult != nullptr) + { + InvokeTxtResolverCallback(aResolver->mCallback, *sInvokeOnStart.mTxtResult); + } +} + +void otPlatDnssdStopTxtResolver(otInstance *aInstance, const otPlatDnssdTxtResolver *aResolver) +{ + VerifyOrQuit(aResolver != nullptr); + + Log("otPlatDnssdStopTxtResolver(\"%s\", \"%s\")", aResolver->mServiceInstance, aResolver->mServiceType); + + VerifyOrQuit(aInstance == sInstance); + VerifyOrQuit(aResolver->mInfraIfIndex == kInfraIfIndex); + + sStopTxtResolverInfo.UpdateFrom(aResolver); +} + +void otPlatDnssdStartIp6AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver) +{ + VerifyOrQuit(aResolver != nullptr); + + Log("otPlatDnssdStartIp6AddressResolver(\"%s\")", aResolver->mHostName); + + VerifyOrQuit(aInstance == sInstance); + VerifyOrQuit(aResolver->mInfraIfIndex == kInfraIfIndex); + + sStartIp6AddrResolverInfo.UpdateFrom(aResolver); + + if (sInvokeOnStart.mIp6AddrResult != nullptr) + { + InvokeIp6AddrResolverCallback(aResolver->mCallback, *sInvokeOnStart.mIp6AddrResult); + } +} + +void otPlatDnssdStopIp6AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver) +{ + VerifyOrQuit(aResolver != nullptr); + + Log("otPlatDnssdStopIp6AddressResolver(\"%s\")", aResolver->mHostName); + + VerifyOrQuit(aInstance == sInstance); + VerifyOrQuit(aResolver->mInfraIfIndex == kInfraIfIndex); + + sStopIp6AddrResolverInfo.UpdateFrom(aResolver); + + if (sInvokeOnStart.mIp6AddrResult != nullptr) + { + InvokeIp6AddrResolverCallback(aResolver->mCallback, *sInvokeOnStart.mIp6AddrResult); + } +} + +//---------------------------------------------------------------------------------------------------------------------- + +void TestProxyBasic(void) +{ + static constexpr uint32_t kTtl = 300; + + const uint8_t kTxtData[] = {3, 'A', '=', '1', 0}; + + Srp::Server *srpServer; + Srp::Client *srpClient; + Dns::Client *dnsClient; + Dns::ServiceDiscovery::Server *dnsServer; + Dnssd::BrowseResult browseResult; + Dnssd::SrvResult srvResult; + Dnssd::TxtResult txtResult; + Dnssd::AddressResult ip6AddrrResult; + Dnssd::AddressAndTtl addressAndTtl; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestProxyBasic"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + dnsClient = &sInstance->Get(); + dnsServer = &sInstance->Get(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true)); + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + sBrowseInfo.Reset(); + ResetPlatDnssdApiInfo(); + + Log("Browse()"); + SuccessOrQuit(dnsClient->Browse("_avenger._udp.default.service.arpa.", BrowseCallback, sInstance)); + AdvanceTime(10); + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStartBrowserInfo.ServiceTypeMatches("_avenger._udp")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke Browser callback"); + + browseResult.mServiceType = "_avenger._udp"; + browseResult.mSubTypeLabel = nullptr; + browseResult.mServiceInstance = "hulk"; + browseResult.mTtl = kTtl; + browseResult.mInfraIfIndex = kInfraIfIndex; + + InvokeBrowserCallback(sStartBrowserInfo.mCallback, browseResult); + + AdvanceTime(10); + + // Check that browser is stopped and a service resolver is started + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStopBrowserInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStopBrowserInfo.mCallback == sStartBrowserInfo.mCallback); + + VerifyOrQuit(sStartSrvResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStartSrvResolverInfo.ServiceInstanceMatches("hulk")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke SRV resolver callback"); + + srvResult.mServiceInstance = "hulk"; + srvResult.mServiceType = "_avenger._udp"; + srvResult.mHostName = "compound"; + srvResult.mPort = 7777; + srvResult.mTtl = kTtl; + srvResult.mInfraIfIndex = kInfraIfIndex; + + InvokeSrvResolverCallback(sStartSrvResolverInfo.mCallback, srvResult); + + AdvanceTime(10); + + // Check that SRV resolver is stopped and a TXT resolver is started + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStopSrvResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStopSrvResolverInfo.ServiceInstanceMatches("hulk")); + VerifyOrQuit(sStopSrvResolverInfo.mCallback == sStartSrvResolverInfo.mCallback); + + VerifyOrQuit(sStartTxtResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStartTxtResolverInfo.ServiceInstanceMatches("hulk")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke TXT resolver callback"); + + txtResult.mServiceInstance = "hulk"; + txtResult.mServiceType = "_avenger._udp"; + txtResult.mTxtData = kTxtData; + txtResult.mTxtDataLength = sizeof(kTxtData); + txtResult.mTtl = kTtl; + txtResult.mInfraIfIndex = kInfraIfIndex; + + InvokeTxtResolverCallback(sStartTxtResolverInfo.mCallback, txtResult); + + AdvanceTime(10); + + // Check that TXT resolver is stopped and an address resolver is started + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStopTxtResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStopTxtResolverInfo.ServiceInstanceMatches("hulk")); + VerifyOrQuit(sStopTxtResolverInfo.mCallback == sStartTxtResolverInfo.mCallback); + + VerifyOrQuit(sStartIp6AddrResolverInfo.HostNameMatches("compound")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke Address Resolver callback"); + + SuccessOrQuit(AsCoreType(&addressAndTtl.mAddress).FromString("fd00::1234")); + addressAndTtl.mTtl = kTtl; + + ip6AddrrResult.mHostName = "compound"; + ip6AddrrResult.mInfraIfIndex = kInfraIfIndex; + ip6AddrrResult.mAddresses = &addressAndTtl; + ip6AddrrResult.mAddressesLength = 1; + + InvokeIp6AddrResolverCallback(sStartIp6AddrResolverInfo.mCallback, ip6AddrrResult); + + AdvanceTime(10); + + // Check that address resolver is stopped + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + + VerifyOrQuit(sStopIp6AddrResolverInfo.HostNameMatches("compound")); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallback == sStartIp6AddrResolverInfo.mCallback); + + // Check that response is sent to client and validate it + + VerifyOrQuit(sBrowseInfo.mCallbackCount == 1); + SuccessOrQuit(sBrowseInfo.mError); + VerifyOrQuit(sBrowseInfo.mNumInstances == 1); + + VerifyOrQuit(!strcmp(sBrowseInfo.mServiceName, "_avenger._udp.default.service.arpa.")); + VerifyOrQuit(!strcmp(sBrowseInfo.mInstanceLabel, "hulk")); + VerifyOrQuit(!strcmp(sBrowseInfo.mServiceInfo.mHostNameBuffer, "compound.default.service.arpa.")); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mPort == 7777); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mTtl == kTtl); + VerifyOrQuit(AsCoreType(&sBrowseInfo.mServiceInfo.mHostAddress) == AsCoreType(&addressAndTtl.mAddress)); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mHostAddressTtl == kTtl); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mTxtDataSize == sizeof(kTxtData)); + VerifyOrQuit(!memcmp(sBrowseInfo.mServiceInfo.mTxtData, kTxtData, sizeof(kTxtData))); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mTxtDataTtl == kTtl); + VerifyOrQuit(!sBrowseInfo.mServiceInfo.mTxtDataTruncated); + + Log("--------------------------------------------------------------------------------------------"); + + ResetPlatDnssdApiInfo(); + sResolveServiceInfo.Reset(); + + Log("ResolveService() with dot `.` character in service instance label"); + SuccessOrQuit( + dnsClient->ResolveService("iron.man", "_avenger._udp.default.service.arpa.", ServiceCallback, sInstance)); + + AdvanceTime(10); + + // Check that a service resolver is started + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStartSrvResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStartSrvResolverInfo.ServiceInstanceMatches("iron.man")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke SRV Resolver callback for wrong name"); + + srvResult.mServiceInstance = "hulk"; + srvResult.mServiceType = "_avenger._udp"; + srvResult.mHostName = "compound"; + srvResult.mPort = 7777; + srvResult.mTtl = kTtl; + srvResult.mInfraIfIndex = kInfraIfIndex; + + InvokeSrvResolverCallback(sStartSrvResolverInfo.mCallback, srvResult); + + AdvanceTime(10); + + // Check that no changes to browsers/resolvers + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 0); + + Log("Invoke SRV Resolver callback for correct name"); + + srvResult.mServiceInstance = "iron.man"; + srvResult.mServiceType = "_avenger._udp"; + srvResult.mHostName = "starktower"; + srvResult.mPort = 1024; + srvResult.mTtl = kTtl; + srvResult.mInfraIfIndex = kInfraIfIndex; + + InvokeSrvResolverCallback(sStartSrvResolverInfo.mCallback, srvResult); + + AdvanceTime(10); + + // Check that SRV resolver is stopped and TXT resolver is started + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 0); + + VerifyOrQuit(sStopSrvResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStopSrvResolverInfo.ServiceInstanceMatches("iron.man")); + VerifyOrQuit(sStopSrvResolverInfo.mCallback == sStartSrvResolverInfo.mCallback); + + VerifyOrQuit(sStartTxtResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStartTxtResolverInfo.ServiceInstanceMatches("iron.man")); + + Log("Invoke TXT Resolver callback"); + + txtResult.mServiceInstance = "iron.man"; + txtResult.mServiceType = "_avenger._udp"; + txtResult.mTxtData = kTxtData; + txtResult.mTxtDataLength = sizeof(kTxtData); + txtResult.mTtl = kTtl; + txtResult.mInfraIfIndex = kInfraIfIndex; + + InvokeTxtResolverCallback(sStartTxtResolverInfo.mCallback, txtResult); + AdvanceTime(10); + + // Check that TXT resolver is stopped and addr resolver is started + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 0); + + VerifyOrQuit(sStopTxtResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStopTxtResolverInfo.ServiceInstanceMatches("iron.man")); + VerifyOrQuit(sStopTxtResolverInfo.mCallback == sStartTxtResolverInfo.mCallback); + + VerifyOrQuit(sStartIp6AddrResolverInfo.HostNameMatches("starktower")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke Address Resolver callback"); + + ip6AddrrResult.mHostName = "starktower"; + + InvokeIp6AddrResolverCallback(sStartIp6AddrResolverInfo.mCallback, ip6AddrrResult); + AdvanceTime(10); + + // Check that address resolver is stopped + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + + VerifyOrQuit(sStopIp6AddrResolverInfo.HostNameMatches("starktower")); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallback == sStartIp6AddrResolverInfo.mCallback); + + // Check that response is sent to client and validate it + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + VerifyOrQuit(sResolveServiceInfo.mError == kErrorNone); + + VerifyOrQuit(!strcmp(sResolveServiceInfo.mInfo.mHostNameBuffer, "starktower.default.service.arpa.")); + VerifyOrQuit(sResolveServiceInfo.mInfo.mPort == 1024); + VerifyOrQuit(sResolveServiceInfo.mInfo.mTtl == kTtl); + VerifyOrQuit(AsCoreType(&sResolveServiceInfo.mInfo.mHostAddress) == AsCoreType(&addressAndTtl.mAddress)); + VerifyOrQuit(sResolveServiceInfo.mInfo.mHostAddressTtl == kTtl); + VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataSize == sizeof(kTxtData)); + VerifyOrQuit(!memcmp(sResolveServiceInfo.mInfo.mTxtData, kTxtData, sizeof(kTxtData))); + VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataTtl == kTtl); + VerifyOrQuit(!sResolveServiceInfo.mInfo.mTxtDataTruncated); + + Log("--------------------------------------------------------------------------------------------"); + + ResetPlatDnssdApiInfo(); + sResolveAddressInfo.Reset(); + + Log("ResolveAddress()"); + SuccessOrQuit(dnsClient->ResolveAddress("earth.default.service.arpa.", AddressCallback, sInstance)); + AdvanceTime(10); + + // Check that an address resolver is started + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStartIp6AddrResolverInfo.HostNameMatches("earth")); + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 0); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke Address Resolver callback"); + + SuccessOrQuit(AsCoreType(&addressAndTtl.mAddress).FromString("fd00::7777")); + addressAndTtl.mTtl = kTtl; + ip6AddrrResult.mHostName = "earth"; + ip6AddrrResult.mInfraIfIndex = kInfraIfIndex; + ip6AddrrResult.mAddresses = &addressAndTtl; + ip6AddrrResult.mAddressesLength = 1; + + InvokeIp6AddrResolverCallback(sStartIp6AddrResolverInfo.mCallback, ip6AddrrResult); + + AdvanceTime(10); + + // Check that the address resolver is stopped + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + + VerifyOrQuit(sStopIp6AddrResolverInfo.HostNameMatches("earth")); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallback == sStartIp6AddrResolverInfo.mCallback); + + // Check that response is sent to client and validate it + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 1); + SuccessOrQuit(sResolveAddressInfo.mError); + + VerifyOrQuit(!strcmp(sResolveAddressInfo.mHostName, "earth.default.service.arpa.")); + VerifyOrQuit(sResolveAddressInfo.mNumHostAddresses == 1); + VerifyOrQuit(sResolveAddressInfo.mHostAddresses[0] == AsCoreType(&addressAndTtl.mAddress)); + VerifyOrQuit(sResolveAddressInfo.mTtl == kTtl); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Stop DNS-SD server"); + + dnsServer->Stop(); + + AdvanceTime(10); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + FinalizeTest(); + + Log("End of TestProxyBasic"); +} + +//---------------------------------------------------------------------------------------------------------------------- + +void TestProxySubtypeBrowse(void) +{ + static constexpr uint32_t kTtl = 300; + + const uint8_t kTxtData[] = {3, 'G', '=', '0', 0}; + + Srp::Server *srpServer; + Srp::Client *srpClient; + Dns::Client *dnsClient; + Dns::ServiceDiscovery::Server *dnsServer; + Dnssd::BrowseResult browseResult; + Dnssd::SrvResult srvResult; + Dnssd::TxtResult txtResult; + Dnssd::AddressResult ip6AddrrResult; + Dnssd::AddressAndTtl addressAndTtl; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestProxySubtypeBrowse"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + dnsClient = &sInstance->Get(); + dnsServer = &sInstance->Get(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true)); + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + sBrowseInfo.Reset(); + ResetPlatDnssdApiInfo(); + + Log("Browse() for sub-type service"); + SuccessOrQuit(dnsClient->Browse("_god._sub._avenger._udp.default.service.arpa.", BrowseCallback, sInstance)); + AdvanceTime(10); + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStartBrowserInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStartBrowserInfo.SubTypeMatches("_god")); + + VerifyOrQuit(sBrowseInfo.mCallbackCount == 0); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke Browser callback"); + + browseResult.mServiceType = "_avenger._udp"; + browseResult.mSubTypeLabel = "_god"; + browseResult.mServiceInstance = "thor"; + browseResult.mTtl = kTtl; + browseResult.mInfraIfIndex = kInfraIfIndex; + + InvokeBrowserCallback(sStartBrowserInfo.mCallback, browseResult); + + AdvanceTime(10); + + // Check that browser is stopped and a service resolver is started + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStopBrowserInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStopBrowserInfo.SubTypeMatches("_god")); + VerifyOrQuit(sStopBrowserInfo.mCallback == sStartBrowserInfo.mCallback); + + // Check that the SRV resolver is correctly using the base service type + // and not the sub-type name + + VerifyOrQuit(sStartSrvResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStartSrvResolverInfo.ServiceInstanceMatches("thor")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke Service Resolver callback"); + + srvResult.mServiceInstance = "thor"; + srvResult.mServiceType = "_avenger._udp"; + srvResult.mHostName = "asgard"; + srvResult.mPort = 1234; + srvResult.mTtl = kTtl; + srvResult.mInfraIfIndex = kInfraIfIndex; + + InvokeSrvResolverCallback(sStartSrvResolverInfo.mCallback, srvResult); + + AdvanceTime(10); + + // Check that SRV resolver is stopped and a TXT resolver is started + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStopSrvResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStopSrvResolverInfo.ServiceInstanceMatches("thor")); + VerifyOrQuit(sStopSrvResolverInfo.mCallback == sStartSrvResolverInfo.mCallback); + + VerifyOrQuit(sStartTxtResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStartTxtResolverInfo.ServiceInstanceMatches("thor")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke Service Resolver callback"); + + txtResult.mServiceInstance = "thor"; + txtResult.mServiceType = "_avenger._udp"; + txtResult.mTxtData = kTxtData; + txtResult.mTxtDataLength = sizeof(kTxtData); + txtResult.mTtl = kTtl; + txtResult.mInfraIfIndex = kInfraIfIndex; + + InvokeTxtResolverCallback(sStartTxtResolverInfo.mCallback, txtResult); + AdvanceTime(10); + + // Check that TXT resolver is stopped and an address resolver is started + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStopTxtResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStopTxtResolverInfo.ServiceInstanceMatches("thor")); + VerifyOrQuit(sStopTxtResolverInfo.mCallback == sStartTxtResolverInfo.mCallback); + + VerifyOrQuit(sStartIp6AddrResolverInfo.HostNameMatches("asgard")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke Address Resolver callback"); + + SuccessOrQuit(AsCoreType(&addressAndTtl.mAddress).FromString("fd00::1234")); + addressAndTtl.mTtl = kTtl; + + ip6AddrrResult.mHostName = "asgard"; + ip6AddrrResult.mInfraIfIndex = kInfraIfIndex; + ip6AddrrResult.mAddresses = &addressAndTtl; + ip6AddrrResult.mAddressesLength = 1; + + InvokeIp6AddrResolverCallback(sStartIp6AddrResolverInfo.mCallback, ip6AddrrResult); + + AdvanceTime(10); + + // Check that address resolver is stopped + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + + VerifyOrQuit(sStopIp6AddrResolverInfo.HostNameMatches("asgard")); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallback == sStartIp6AddrResolverInfo.mCallback); + + // Check that response is sent to client and validate it + + VerifyOrQuit(sBrowseInfo.mCallbackCount == 1); + SuccessOrQuit(sBrowseInfo.mError); + VerifyOrQuit(sBrowseInfo.mNumInstances == 1); + + VerifyOrQuit(!strcmp(sBrowseInfo.mServiceName, "_god._sub._avenger._udp.default.service.arpa.")); + VerifyOrQuit(!strcmp(sBrowseInfo.mInstanceLabel, "thor")); + VerifyOrQuit(!strcmp(sBrowseInfo.mServiceInfo.mHostNameBuffer, "asgard.default.service.arpa.")); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mPort == 1234); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mTtl == kTtl); + VerifyOrQuit(AsCoreType(&sBrowseInfo.mServiceInfo.mHostAddress) == AsCoreType(&addressAndTtl.mAddress)); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mHostAddressTtl == kTtl); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mTxtDataSize == sizeof(kTxtData)); + VerifyOrQuit(!memcmp(sBrowseInfo.mServiceInfo.mTxtData, kTxtData, sizeof(kTxtData))); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mTxtDataTtl == kTtl); + VerifyOrQuit(!sBrowseInfo.mServiceInfo.mTxtDataTruncated); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Stop DNS-SD server"); + + dnsServer->Stop(); + + AdvanceTime(10); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + FinalizeTest(); + + Log("End of TestProxySubtypeBrowse"); +} + +//---------------------------------------------------------------------------------------------------------------------- + +void TestProxyTimeout(void) +{ + static constexpr uint32_t kTtl = 300; + + Srp::Server *srpServer; + Srp::Client *srpClient; + Dns::Client *dnsClient; + Dns::ServiceDiscovery::Server *dnsServer; + Dns::Client::QueryConfig config; + Dnssd::BrowseResult browseResult; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestProxyTimeout"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + dnsClient = &sInstance->Get(); + dnsServer = &sInstance->Get(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true)); + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Increase default response retry timeout on DNS client"); + + config.Clear(); + config.mResponseTimeout = 120 * 1000; // 2 minutes (in msec) + dnsClient->SetDefaultConfig(config); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + sBrowseInfo.Reset(); + ResetPlatDnssdApiInfo(); + + Log("Browse()"); + SuccessOrQuit(dnsClient->Browse("_game._ps5.default.service.arpa.", BrowseCallback, sInstance)); + AdvanceTime(10); + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStartBrowserInfo.ServiceTypeMatches("_game._ps5")); + + VerifyOrQuit(sBrowseInfo.mCallbackCount == 0); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Wait for timeout and check empty response on client"); + + AdvanceTime(10 * 1000); + + VerifyOrQuit(sBrowseInfo.mCallbackCount == 1); + VerifyOrQuit(sBrowseInfo.mNumInstances == 0); + + // Check that the browser is stopped + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + + VerifyOrQuit(sStopBrowserInfo.ServiceTypeMatches("_game._ps5")); + VerifyOrQuit(sStopBrowserInfo.mCallback == sStartBrowserInfo.mCallback); + + Log("--------------------------------------------------------------------------------------------"); + Log("Timeout during service resolution"); + + sBrowseInfo.Reset(); + ResetPlatDnssdApiInfo(); + + SuccessOrQuit(dnsClient->Browse("_avenger._udp.default.service.arpa.", BrowseCallback, sInstance)); + AdvanceTime(10); + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStartBrowserInfo.ServiceTypeMatches("_avenger._udp")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke Browser callback"); + + browseResult.mServiceType = "_avenger._udp"; + browseResult.mSubTypeLabel = nullptr; + browseResult.mServiceInstance = "spiderman"; + browseResult.mTtl = kTtl; + browseResult.mInfraIfIndex = kInfraIfIndex; + + InvokeBrowserCallback(sStartBrowserInfo.mCallback, browseResult); + + AdvanceTime(10); + + // Check that browser is stopped and a service resolver is started + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStopBrowserInfo.ServiceTypeMatches("_avenger._udp")); + + VerifyOrQuit(sStartSrvResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStartSrvResolverInfo.ServiceInstanceMatches("spiderman")); + + VerifyOrQuit(sBrowseInfo.mCallbackCount == 0); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Wait for timeout"); + + AdvanceTime(10 * 1000); + + VerifyOrQuit(sBrowseInfo.mCallbackCount == 1); + VerifyOrQuit(sBrowseInfo.mNumInstances == 1); + + // Check that the browser is stopped + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStopBrowserInfo.ServiceTypeMatches("_avenger._udp")); + + // Validate the response received by client + + VerifyOrQuit(!strcmp(sBrowseInfo.mServiceName, "_avenger._udp.default.service.arpa.")); + VerifyOrQuit(!strcmp(sBrowseInfo.mInstanceLabel, "spiderman")); + + Log("--------------------------------------------------------------------------------------------"); + Log("Timeout during multiple requests"); + + sBrowseInfo.Reset(); + sResolveServiceInfo.Reset(); + sResolveAddressInfo.Reset(); + ResetPlatDnssdApiInfo(); + + Log("Browse()"); + SuccessOrQuit(dnsClient->Browse("_avenger._udp.default.service.arpa.", BrowseCallback, sInstance)); + AdvanceTime(10); + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStartBrowserInfo.ServiceTypeMatches("_avenger._udp")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Another Browse()"); + SuccessOrQuit(dnsClient->Browse("_game._udp.default.service.arpa.", BrowseCallback, sInstance)); + AdvanceTime(10); + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 2); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStartBrowserInfo.ServiceTypeMatches("_game._udp")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("ResolveService()"); + SuccessOrQuit( + dnsClient->ResolveService("wanda", "_avenger._udp.default.service.arpa.", ServiceCallback, sInstance)); + AdvanceTime(10); + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 2); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStartSrvResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStartSrvResolverInfo.ServiceInstanceMatches("wanda")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("ResolveAddress()"); + SuccessOrQuit(dnsClient->ResolveAddress("earth.default.service.arpa.", AddressCallback, sInstance)); + AdvanceTime(10); + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 2); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStartIp6AddrResolverInfo.HostNameMatches("earth")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Wait for timeout for all requests"); + + AdvanceTime(10 * 1000); + + VerifyOrQuit(sBrowseInfo.mCallbackCount == 2); + VerifyOrQuit(sBrowseInfo.mNumInstances == 0); + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + VerifyOrQuit(sResolveServiceInfo.mError == kErrorNotFound); + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 1); + VerifyOrQuit(sResolveAddressInfo.mNumHostAddresses == 0); + + // Check that all browsers/resolvers are stopped. + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 2); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 2); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + + VerifyOrQuit(sStopSrvResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStopSrvResolverInfo.ServiceInstanceMatches("wanda")); + VerifyOrQuit(sStopIp6AddrResolverInfo.HostNameMatches("earth")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Stop DNS-SD server"); + + dnsServer->Stop(); + + AdvanceTime(10); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + FinalizeTest(); + + Log("End of TestProxyTimeout"); +} + +void TestProxySharedResolver(void) +{ + static constexpr uint32_t kTtl = 300; + + const uint8_t kTxtData[] = {3, 'A', '=', '1', 0}; + + Srp::Server *srpServer; + Srp::Client *srpClient; + Dns::Client *dnsClient; + Dns::ServiceDiscovery::Server *dnsServer; + Dnssd::BrowseResult browseResult; + Dnssd::SrvResult srvResult; + Dnssd::TxtResult txtResult; + Dnssd::AddressResult ip6AddrrResult; + Dnssd::AddressAndTtl addressAndTtl[2]; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestProxySharedResolver"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + dnsClient = &sInstance->Get(); + dnsServer = &sInstance->Get(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true)); + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + sBrowseInfo.Reset(); + sResolveServiceInfo.Reset(); + sResolveAddressInfo.Reset(); + ResetPlatDnssdApiInfo(); + + Log("ResolveAddress()"); + SuccessOrQuit(dnsClient->ResolveAddress("knowhere.default.service.arpa.", AddressCallback, sInstance)); + AdvanceTime(10); + + Log("ResolveService()"); + SuccessOrQuit( + dnsClient->ResolveService("starlord", "_guardian._glaxy.default.service.arpa.", ServiceCallback, sInstance)); + AdvanceTime(10); + + Log("Browse()"); + SuccessOrQuit(dnsClient->Browse("_guardian._glaxy.default.service.arpa.", BrowseCallback, sInstance)); + AdvanceTime(10); + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStartBrowserInfo.ServiceTypeMatches("_guardian._glaxy")); + + VerifyOrQuit(sStartSrvResolverInfo.ServiceTypeMatches("_guardian._glaxy")); + VerifyOrQuit(sStartSrvResolverInfo.ServiceInstanceMatches("starlord")); + + VerifyOrQuit(sStartIp6AddrResolverInfo.HostNameMatches("knowhere")); + + VerifyOrQuit(sBrowseInfo.mCallbackCount == 0); + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 0); + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 0); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke Browser callback"); + + browseResult.mServiceType = "_guardian._glaxy"; + browseResult.mSubTypeLabel = nullptr; + browseResult.mServiceInstance = "starlord"; + browseResult.mTtl = kTtl; + browseResult.mInfraIfIndex = kInfraIfIndex; + + InvokeBrowserCallback(sStartBrowserInfo.mCallback, browseResult); + + AdvanceTime(10); + + // Check that browser is stopped and since the service instance + // name matches an existing resolver, we should not see any new + // resolver starting. + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStopBrowserInfo.ServiceTypeMatches("_guardian._glaxy")); + + VerifyOrQuit(sBrowseInfo.mCallbackCount == 0); + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 0); + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 0); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke Service Resolver callback"); + + srvResult.mServiceInstance = "starlord"; + srvResult.mServiceType = "_guardian._glaxy"; + srvResult.mHostName = "knowhere"; + srvResult.mPort = 3333; + srvResult.mTtl = kTtl; + srvResult.mInfraIfIndex = kInfraIfIndex; + + InvokeSrvResolverCallback(sStartSrvResolverInfo.mCallback, srvResult); + + AdvanceTime(10); + + // Check that SRV resolver is now stopped and a TXT resolver + // is started for same service. + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStopSrvResolverInfo.ServiceTypeMatches("_guardian._glaxy")); + VerifyOrQuit(sStopSrvResolverInfo.ServiceInstanceMatches("starlord")); + + txtResult.mServiceInstance = "starlord"; + txtResult.mServiceType = "_guardian._glaxy"; + txtResult.mTxtData = kTxtData; + txtResult.mTxtDataLength = sizeof(kTxtData); + txtResult.mTtl = kTtl; + txtResult.mInfraIfIndex = kInfraIfIndex; + + InvokeTxtResolverCallback(sStartTxtResolverInfo.mCallback, txtResult); + + AdvanceTime(10); + + // Check that TXT resolver is now stopped but again since the + // host name matches an existing address resolver we should not + // see any new address resolver. + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStopTxtResolverInfo.ServiceTypeMatches("_guardian._glaxy")); + VerifyOrQuit(sStopTxtResolverInfo.ServiceInstanceMatches("starlord")); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke Address Resolver callback"); + + SuccessOrQuit(AsCoreType(&addressAndTtl[0].mAddress).FromString("fd00::5555")); + SuccessOrQuit(AsCoreType(&addressAndTtl[1].mAddress).FromString("fd00::1234")); + addressAndTtl[0].mTtl = kTtl; + addressAndTtl[1].mTtl = kTtl; + + ip6AddrrResult.mHostName = "knowhere"; + ip6AddrrResult.mInfraIfIndex = kInfraIfIndex; + ip6AddrrResult.mAddresses = addressAndTtl; + ip6AddrrResult.mAddressesLength = 2; + + InvokeIp6AddrResolverCallback(sStartIp6AddrResolverInfo.mCallback, ip6AddrrResult); + + AdvanceTime(10); + + // Check that the address resolver is now stopped. + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + + VerifyOrQuit(sStopIp6AddrResolverInfo.HostNameMatches("knowhere")); + + // Check the browse response received on client + + VerifyOrQuit(sBrowseInfo.mCallbackCount == 1); + SuccessOrQuit(sBrowseInfo.mError); + VerifyOrQuit(sBrowseInfo.mNumInstances == 1); + + VerifyOrQuit(!strcmp(sBrowseInfo.mServiceName, "_guardian._glaxy.default.service.arpa.")); + VerifyOrQuit(!strcmp(sBrowseInfo.mInstanceLabel, "starlord")); + VerifyOrQuit(!strcmp(sBrowseInfo.mServiceInfo.mHostNameBuffer, "knowhere.default.service.arpa.")); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mPort == 3333); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mTtl == kTtl); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mHostAddressTtl == kTtl); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mTxtDataSize == sizeof(kTxtData)); + VerifyOrQuit(!memcmp(sBrowseInfo.mServiceInfo.mTxtData, kTxtData, sizeof(kTxtData))); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mTxtDataTtl == kTtl); + VerifyOrQuit(!sBrowseInfo.mServiceInfo.mTxtDataTruncated); + + // Check the service resolve response received on client + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + SuccessOrQuit(sResolveServiceInfo.mError); + + VerifyOrQuit(!strcmp(sResolveServiceInfo.mInfo.mHostNameBuffer, "knowhere.default.service.arpa.")); + VerifyOrQuit(sResolveServiceInfo.mInfo.mPort == 3333); + VerifyOrQuit(sResolveServiceInfo.mInfo.mTtl == kTtl); + VerifyOrQuit(sResolveServiceInfo.mInfo.mHostAddressTtl == kTtl); + VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataSize == sizeof(kTxtData)); + VerifyOrQuit(!memcmp(sResolveServiceInfo.mInfo.mTxtData, kTxtData, sizeof(kTxtData))); + VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataTtl == kTtl); + VerifyOrQuit(!sResolveServiceInfo.mInfo.mTxtDataTruncated); + VerifyOrQuit(sResolveServiceInfo.mNumHostAddresses == 2); + for (uint16_t index = 0; index < 2; index++) + { + VerifyOrQuit(sResolveServiceInfo.mHostAddresses[index] == AsCoreType(&addressAndTtl[0].mAddress) || + sResolveServiceInfo.mHostAddresses[index] == AsCoreType(&addressAndTtl[1].mAddress)); + } + + // Check the address resolve response received on client + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 1); + SuccessOrQuit(sResolveAddressInfo.mError); + + VerifyOrQuit(!strcmp(sResolveAddressInfo.mHostName, "knowhere.default.service.arpa.")); + VerifyOrQuit(sResolveAddressInfo.mTtl == kTtl); + VerifyOrQuit(sResolveAddressInfo.mNumHostAddresses == 2); + for (uint16_t index = 0; index < 2; index++) + { + VerifyOrQuit(sResolveAddressInfo.mHostAddresses[index] == AsCoreType(&addressAndTtl[0].mAddress) || + sResolveAddressInfo.mHostAddresses[index] == AsCoreType(&addressAndTtl[1].mAddress)); + } + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Stop DNS-SD server"); + + dnsServer->Stop(); + + AdvanceTime(10); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + FinalizeTest(); + + Log("End of TestProxySharedResolver"); +} + +void TestProxyFilterInvalidAddresses(void) +{ + static constexpr uint32_t kTtl = 300; + + Srp::Server *srpServer; + Srp::Client *srpClient; + Dns::Client *dnsClient; + Dns::ServiceDiscovery::Server *dnsServer; + Dnssd::AddressResult ip6AddrrResult; + Dnssd::AddressAndTtl addressAndTtl[10]; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestProxyFilterInvalidAddresses"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + dnsClient = &sInstance->Get(); + dnsServer = &sInstance->Get(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true)); + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + sResolveAddressInfo.Reset(); + ResetPlatDnssdApiInfo(); + + Log("ResolveAddress()"); + SuccessOrQuit(dnsClient->ResolveAddress("host.default.service.arpa.", AddressCallback, sInstance)); + AdvanceTime(10); + + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStartIp6AddrResolverInfo.HostNameMatches("host")); + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 0); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke Address Resolver callback with invalid addresses"); + + SuccessOrQuit(AsCoreType(&addressAndTtl[0].mAddress).FromString("::")); // Unspecified + SuccessOrQuit(AsCoreType(&addressAndTtl[1].mAddress).FromString("fe80::1234")); // Link local + SuccessOrQuit(AsCoreType(&addressAndTtl[2].mAddress).FromString("ff00::1234")); // Multicast + SuccessOrQuit(AsCoreType(&addressAndTtl[3].mAddress).FromString("::1")); // Lookback + + for (uint16_t index = 0; index < 5; index++) + { + addressAndTtl[index].mTtl = kTtl; + } + + ip6AddrrResult.mHostName = "host"; + ip6AddrrResult.mInfraIfIndex = kInfraIfIndex; + ip6AddrrResult.mAddresses = addressAndTtl; + ip6AddrrResult.mAddressesLength = 4; + + InvokeIp6AddrResolverCallback(sStartIp6AddrResolverInfo.mCallback, ip6AddrrResult); + + AdvanceTime(10); + + // Check that the address resolver is not stopped, since all addresses where + // invalid address. + + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 0); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Invoke Address Resolver callback with invalid addresses with one valid"); + + SuccessOrQuit(AsCoreType(&addressAndTtl[4].mAddress).FromString("f00::1234")); + + ip6AddrrResult.mAddressesLength = 5; + + InvokeIp6AddrResolverCallback(sStartIp6AddrResolverInfo.mCallback, ip6AddrrResult); + + AdvanceTime(10); + + // Check that address resolver is not stopped + + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + + VerifyOrQuit(sStopIp6AddrResolverInfo.HostNameMatches("host")); + + // Check that response received on client is valid and only contains + // the valid two addresses and filters all others. + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 1); + SuccessOrQuit(sResolveAddressInfo.mError); + + VerifyOrQuit(!strcmp(sResolveAddressInfo.mHostName, "host.default.service.arpa.")); + VerifyOrQuit(sResolveAddressInfo.mTtl == kTtl); + VerifyOrQuit(sResolveAddressInfo.mNumHostAddresses == 1); + VerifyOrQuit(sResolveAddressInfo.mHostAddresses[0] == AsCoreType(&addressAndTtl[4].mAddress)); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Stop DNS-SD server"); + + dnsServer->Stop(); + + AdvanceTime(10); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + FinalizeTest(); + + Log("End of TestProxyFilterInvalidAddresses"); +} + +void TestProxyStateChanges(void) +{ + static constexpr uint32_t kTtl = 300; + + Srp::Server *srpServer; + Srp::Client *srpClient; + Dns::Client *dnsClient; + Dns::ServiceDiscovery::Server *dnsServer; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestProxyStateChanges"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + dnsClient = &sInstance->Get(); + dnsServer = &sInstance->Get(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true)); + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Signal DNS-SD platform state is stopped and not yet ready"); + + sDnssdState = OT_PLAT_DNSSD_STOPPED; + otPlatDnssdStateHandleStateChange(sInstance); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + sResolveAddressInfo.Reset(); + ResetPlatDnssdApiInfo(); + + Log("ResolveAddress()"); + SuccessOrQuit(dnsClient->ResolveAddress("host.default.service.arpa.", AddressCallback, sInstance)); + AdvanceTime(10); + + // Check that none of the DNS-SD resolver/browser APIs are called + // since the platform is not yet ready + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 1); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Signal DNS-SD platform state is now ready"); + + sDnssdState = OT_PLAT_DNSSD_READY; + otPlatDnssdStateHandleStateChange(sInstance); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + sResolveAddressInfo.Reset(); + ResetPlatDnssdApiInfo(); + + Log("ResolveAddress()"); + SuccessOrQuit(dnsClient->ResolveAddress("host.default.service.arpa.", AddressCallback, sInstance)); + AdvanceTime(10); + + // Check that address resolver is started + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStartIp6AddrResolverInfo.HostNameMatches("host")); + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 0); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + sBrowseInfo.Reset(); + + Log("Browse()"); + SuccessOrQuit(dnsClient->Browse("_magic._udp.default.service.arpa.", BrowseCallback, sInstance)); + AdvanceTime(10); + + // Check that browser is also started + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 0); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 0); + + VerifyOrQuit(sStartBrowserInfo.ServiceTypeMatches("_magic._udp")); + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 0); + VerifyOrQuit(sBrowseInfo.mCallbackCount == 0); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Signal infra-if is not running"); + + SuccessOrQuit(otPlatInfraIfStateChanged(sInstance, kInfraIfIndex, /* aIsRunning */ false)); + + AdvanceTime(10); + + // Check that both address resolver and browser are stopped + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + + VerifyOrQuit(sStopIp6AddrResolverInfo.HostNameMatches("host")); + VerifyOrQuit(sStopBrowserInfo.ServiceTypeMatches("_magic._udp")); + + // And response is sent to client + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 1); + VerifyOrQuit(sBrowseInfo.mCallbackCount == 1); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + sResolveAddressInfo.Reset(); + + Log("ResolveAddress()"); + SuccessOrQuit(dnsClient->ResolveAddress("earth.default.service.arpa.", AddressCallback, sInstance)); + AdvanceTime(10); + + // Check that no resolver is started. + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 1); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Signal that infra-if is running again "); + + SuccessOrQuit(otPlatInfraIfStateChanged(sInstance, kInfraIfIndex, /* aIsRunning */ true)); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + sResolveServiceInfo.Reset(); + Log("ResolveService()"); + SuccessOrQuit(dnsClient->ResolveService("captain.america", "_avenger._udp.default.service.arpa.", ServiceCallback, + sInstance)); + AdvanceTime(10); + + // The proxy should be started again so check that a service resolver + // is started for new request + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + + VerifyOrQuit(sStartSrvResolverInfo.ServiceTypeMatches("_avenger._udp")); + VerifyOrQuit(sStartSrvResolverInfo.ServiceInstanceMatches("captain.america")); + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 0); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Signal DNS-SD platform state is stopped"); + + sDnssdState = OT_PLAT_DNSSD_STOPPED; + otPlatDnssdStateHandleStateChange(sInstance); + + AdvanceTime(10); + + // This should stop proxy but since DNS-SD platform is stopped + // we assume all browsers/resolvers are also stopped, so there + // should be no explicit call to stop it. + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + + // Check that response is sent to client + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Signal DNS-SD platform state is ready again"); + + sDnssdState = OT_PLAT_DNSSD_READY; + otPlatDnssdStateHandleStateChange(sInstance); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + sBrowseInfo.Reset(); + + Log("Browse()"); + SuccessOrQuit(dnsClient->Browse("_magical._udp.default.service.arpa.", BrowseCallback, sInstance)); + AdvanceTime(10); + + // Proxy should be started again and we should see a new browser started + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 2); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + + VerifyOrQuit(sStartBrowserInfo.ServiceTypeMatches("_magical._udp")); + + VerifyOrQuit(sBrowseInfo.mCallbackCount == 0); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Stop DNS-SD server"); + + dnsServer->Stop(); + + AdvanceTime(10); + + // Check that the browser is stopped + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 2); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 2); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 0); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + + VerifyOrQuit(sStopBrowserInfo.ServiceTypeMatches("_magical._udp")); + + // And response is sent to client + + VerifyOrQuit(sBrowseInfo.mCallbackCount == 1); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + FinalizeTest(); + + Log("End of TestProxyStateChanges"); +} + +void TestProxyInvokeCallbackFromStartApi(void) +{ + static constexpr uint32_t kTtl = 300; + + const uint8_t kTxtData[] = {3, 'A', '=', '1', 0}; + + Srp::Server *srpServer; + Srp::Client *srpClient; + Dns::Client *dnsClient; + Dns::ServiceDiscovery::Server *dnsServer; + Dnssd::BrowseResult browseResult; + Dnssd::SrvResult srvResult; + Dnssd::TxtResult txtResult; + Dnssd::AddressResult ip6AddrrResult; + Dnssd::AddressAndTtl addressAndTtl[2]; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestProxyInvokeCallbackFromStartApi"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + dnsClient = &sInstance->Get(); + dnsServer = &sInstance->Get(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(otBorderRoutingInit(sInstance, /* aInfraIfIndex */ kInfraIfIndex, /* aInfraIfIsRunning */ true)); + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Enable invoking of callback directly from otPlatDnssdStart{Browsers/Resolver} APIs"); + + ResetPlatDnssdApiInfo(); + + sInvokeOnStart.mBrowseResult = &browseResult; + sInvokeOnStart.mSrvResult = &srvResult; + sInvokeOnStart.mTxtResult = &txtResult; + sInvokeOnStart.mIp6AddrResult = &ip6AddrrResult; + + browseResult.mServiceType = "_guardian._glaxy"; + browseResult.mSubTypeLabel = nullptr; + browseResult.mServiceInstance = "mantis"; + browseResult.mTtl = kTtl; + browseResult.mInfraIfIndex = kInfraIfIndex; + + srvResult.mServiceInstance = "mantis"; + srvResult.mServiceType = "_guardian._glaxy"; + srvResult.mHostName = "nova"; + srvResult.mPort = 3333; + srvResult.mTtl = kTtl; + srvResult.mInfraIfIndex = kInfraIfIndex; + + txtResult.mServiceInstance = "mantis"; + txtResult.mServiceType = "_guardian._glaxy"; + txtResult.mTxtData = kTxtData; + txtResult.mTxtDataLength = sizeof(kTxtData); + txtResult.mTtl = kTtl; + txtResult.mInfraIfIndex = kInfraIfIndex; + + SuccessOrQuit(AsCoreType(&addressAndTtl[0].mAddress).FromString("fd00::5555")); + SuccessOrQuit(AsCoreType(&addressAndTtl[1].mAddress).FromString("fd00::1234")); + addressAndTtl[0].mTtl = kTtl; + addressAndTtl[1].mTtl = kTtl; + + ip6AddrrResult.mHostName = "nova"; + ip6AddrrResult.mInfraIfIndex = kInfraIfIndex; + ip6AddrrResult.mAddresses = addressAndTtl; + ip6AddrrResult.mAddressesLength = 2; + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + sBrowseInfo.Reset(); + + Log("Browse()"); + SuccessOrQuit(dnsClient->Browse("_guardian._glaxy.default.service.arpa.", BrowseCallback, sInstance)); + AdvanceTime(10); + + // All browsers/resolvers should be started and stopped + // (since the callbacks are invoked directly from API) + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 1); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 1); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 1); + + VerifyOrQuit(sStartBrowserInfo.ServiceTypeMatches("_guardian._glaxy")); + VerifyOrQuit(sStopBrowserInfo.ServiceTypeMatches("_guardian._glaxy")); + + VerifyOrQuit(sStartSrvResolverInfo.ServiceTypeMatches("_guardian._glaxy")); + VerifyOrQuit(sStartSrvResolverInfo.ServiceInstanceMatches("mantis")); + VerifyOrQuit(sStopSrvResolverInfo.ServiceTypeMatches("_guardian._glaxy")); + VerifyOrQuit(sStopSrvResolverInfo.ServiceInstanceMatches("mantis")); + + VerifyOrQuit(sStartTxtResolverInfo.ServiceTypeMatches("_guardian._glaxy")); + VerifyOrQuit(sStartTxtResolverInfo.ServiceInstanceMatches("mantis")); + VerifyOrQuit(sStopTxtResolverInfo.ServiceTypeMatches("_guardian._glaxy")); + VerifyOrQuit(sStopTxtResolverInfo.ServiceInstanceMatches("mantis")); + + VerifyOrQuit(sStartIp6AddrResolverInfo.HostNameMatches("nova")); + VerifyOrQuit(sStopIp6AddrResolverInfo.HostNameMatches("nova")); + + // Check that response is revived by client and validate it + + VerifyOrQuit(sBrowseInfo.mCallbackCount == 1); + + VerifyOrQuit(!strcmp(sBrowseInfo.mServiceName, "_guardian._glaxy.default.service.arpa.")); + VerifyOrQuit(!strcmp(sBrowseInfo.mInstanceLabel, "mantis")); + VerifyOrQuit(!strcmp(sBrowseInfo.mServiceInfo.mHostNameBuffer, "nova.default.service.arpa.")); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mPort == 3333); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mTtl == kTtl); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mHostAddressTtl == kTtl); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mTxtDataSize == sizeof(kTxtData)); + VerifyOrQuit(!memcmp(sBrowseInfo.mServiceInfo.mTxtData, kTxtData, sizeof(kTxtData))); + VerifyOrQuit(sBrowseInfo.mServiceInfo.mTxtDataTtl == kTtl); + VerifyOrQuit(!sBrowseInfo.mServiceInfo.mTxtDataTruncated); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + sResolveServiceInfo.Reset(); + Log("ResolveService()"); + SuccessOrQuit( + dnsClient->ResolveService("mantis", "_guardian._glaxy.default.service.arpa.", ServiceCallback, sInstance)); + AdvanceTime(10); + + // Check that new SRV/TXT resolver and address resolvers are + // started and stopped. + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 2); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 2); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 2); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 2); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 2); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 2); + + VerifyOrQuit(sStartSrvResolverInfo.ServiceTypeMatches("_guardian._glaxy")); + VerifyOrQuit(sStartSrvResolverInfo.ServiceInstanceMatches("mantis")); + VerifyOrQuit(sStopSrvResolverInfo.ServiceTypeMatches("_guardian._glaxy")); + VerifyOrQuit(sStopSrvResolverInfo.ServiceInstanceMatches("mantis")); + + VerifyOrQuit(sStartTxtResolverInfo.ServiceTypeMatches("_guardian._glaxy")); + VerifyOrQuit(sStartTxtResolverInfo.ServiceInstanceMatches("mantis")); + VerifyOrQuit(sStopTxtResolverInfo.ServiceTypeMatches("_guardian._glaxy")); + VerifyOrQuit(sStopTxtResolverInfo.ServiceInstanceMatches("mantis")); + + VerifyOrQuit(sStartIp6AddrResolverInfo.HostNameMatches("nova")); + VerifyOrQuit(sStopIp6AddrResolverInfo.HostNameMatches("nova")); + + // Check the service resolve response received on client + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + SuccessOrQuit(sResolveServiceInfo.mError); + + VerifyOrQuit(!strcmp(sResolveServiceInfo.mInfo.mHostNameBuffer, "nova.default.service.arpa.")); + VerifyOrQuit(sResolveServiceInfo.mInfo.mPort == 3333); + VerifyOrQuit(sResolveServiceInfo.mInfo.mTtl == kTtl); + VerifyOrQuit(sResolveServiceInfo.mInfo.mHostAddressTtl == kTtl); + VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataSize == sizeof(kTxtData)); + VerifyOrQuit(!memcmp(sResolveServiceInfo.mInfo.mTxtData, kTxtData, sizeof(kTxtData))); + VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataTtl == kTtl); + VerifyOrQuit(!sResolveServiceInfo.mInfo.mTxtDataTruncated); + VerifyOrQuit(sResolveServiceInfo.mNumHostAddresses == 2); + for (uint16_t index = 0; index < 2; index++) + { + VerifyOrQuit(sResolveServiceInfo.mHostAddresses[index] == AsCoreType(&addressAndTtl[0].mAddress) || + sResolveServiceInfo.mHostAddresses[index] == AsCoreType(&addressAndTtl[1].mAddress)); + } + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + sResolveAddressInfo.Reset(); + Log("ResolveAddress()"); + SuccessOrQuit(dnsClient->ResolveAddress("nova.default.service.arpa.", AddressCallback, sInstance)); + AdvanceTime(10); + + // Check that new address resolver is started and stopped. + + VerifyOrQuit(sStartBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStopBrowserInfo.mCallCount == 1); + VerifyOrQuit(sStartSrvResolverInfo.mCallCount == 2); + VerifyOrQuit(sStopSrvResolverInfo.mCallCount == 2); + VerifyOrQuit(sStartTxtResolverInfo.mCallCount == 2); + VerifyOrQuit(sStopTxtResolverInfo.mCallCount == 2); + VerifyOrQuit(sStartIp6AddrResolverInfo.mCallCount == 3); + VerifyOrQuit(sStopIp6AddrResolverInfo.mCallCount == 3); + + VerifyOrQuit(sStartIp6AddrResolverInfo.HostNameMatches("nova")); + VerifyOrQuit(sStopIp6AddrResolverInfo.HostNameMatches("nova")); + + // Check the address resolve response received on client + + VerifyOrQuit(sResolveAddressInfo.mCallbackCount == 1); + SuccessOrQuit(sResolveAddressInfo.mError); + + VerifyOrQuit(!strcmp(sResolveAddressInfo.mHostName, "nova.default.service.arpa.")); + VerifyOrQuit(sResolveAddressInfo.mTtl == kTtl); + VerifyOrQuit(sResolveAddressInfo.mNumHostAddresses == 2); + for (uint16_t index = 0; index < 2; index++) + { + VerifyOrQuit(sResolveAddressInfo.mHostAddresses[index] == AsCoreType(&addressAndTtl[0].mAddress) || + sResolveAddressInfo.mHostAddresses[index] == AsCoreType(&addressAndTtl[1].mAddress)); + } + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("Stop DNS-SD server"); + + dnsServer->Stop(); + + AdvanceTime(10); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + FinalizeTest(); + + Log("End of TestProxyInvokeCallbackFromStartApi"); +} + +#endif // ENABLE_DISCOVERY_PROXY_TEST + +int main(void) +{ +#if ENABLE_DISCOVERY_PROXY_TEST + TestProxyBasic(); + TestProxySubtypeBrowse(); + TestProxyTimeout(); + TestProxySharedResolver(); + TestProxyFilterInvalidAddresses(); + TestProxyStateChanges(); + TestProxyInvokeCallbackFromStartApi(); + + printf("All tests passed\n"); +#else + printf("DISCOVERY_PROXY feature or a related feature required by this unit test is not enabled\n"); +#endif + + return 0; +} diff --git a/tests/unit/test_mdns.cpp b/tests/unit/test_mdns.cpp index 1dd114e96..2f1fb562b 100644 --- a/tests/unit/test_mdns.cpp +++ b/tests/unit/test_mdns.cpp @@ -62,17 +62,20 @@ namespace Multicast { //--------------------------------------------------------------------------------------------------------------------- // Constants -static constexpr uint16_t kClassQueryUnicastFlag = (1U << 15); -static constexpr uint16_t kClassCacheFlushFlag = (1U << 15); -static constexpr uint16_t kClassMask = 0x7fff; -static constexpr uint16_t kStringSize = 300; -static constexpr uint16_t kMaxDataSize = 400; -static constexpr uint16_t kNumAnnounces = 3; -static constexpr uint16_t kNumInitalQueries = 3; -static constexpr uint16_t kNumRefreshQueries = 4; -static constexpr bool kCacheFlush = true; -static constexpr uint16_t kMdnsPort = 5353; -static constexpr uint32_t kInfraIfIndex = 1; +static constexpr uint16_t kClassQueryUnicastFlag = (1U << 15); +static constexpr uint16_t kClassCacheFlushFlag = (1U << 15); +static constexpr uint16_t kClassMask = 0x7fff; +static constexpr uint16_t kStringSize = 300; +static constexpr uint16_t kMaxDataSize = 400; +static constexpr uint16_t kNumAnnounces = 3; +static constexpr uint16_t kNumInitalQueries = 3; +static constexpr uint16_t kNumRefreshQueries = 4; +static constexpr bool kCacheFlush = true; +static constexpr uint16_t kMdnsPort = 5353; +static constexpr uint16_t kEphemeralPort = 49152; +static constexpr uint16_t kLegacyUnicastMessageId = 1; +static constexpr uint16_t kMaxLegacyUnicastTtl = 10; +static constexpr uint32_t kInfraIfIndex = 1; static const char kDeviceIp6Address[] = "fd01::1"; @@ -209,6 +212,7 @@ enum TtlCheckMode : uint8_t { kZeroTtl, kNonZeroTtl, + kLegacyUnicastTtl, }; enum Section : uint8_t @@ -372,6 +376,9 @@ struct DnsRecord : public Allocatable, public LinkedListEntry 0); break; + case kLegacyUnicastTtl: + VerifyOrQuit(mTtl <= kMaxLegacyUnicastTtl); + break; } matches = true; @@ -571,6 +578,7 @@ enum DnsMessageType : uint8_t kMulticastQuery, kMulticastResponse, kUnicastResponse, + kLegacyUnicastResponse, }; struct DnsMessage : public Allocatable, public LinkedListEntry @@ -682,9 +690,15 @@ struct DnsMessage : public Allocatable, public LinkedListEntry, public LinkedListEntry, public LinkedListEntry, public LinkedListEntry, public LinkedListEntry, public LinkedListEntry, public LinkedListEntry, public LinkedListEntrymType = kUnicastResponse; + msg->mType = (aUnicastDest->mPort == kEphemeralPort) ? kLegacyUnicastResponse : kUnicastResponse; msg->mUnicastDest = *aUnicastDest; } } @@ -1045,8 +1090,9 @@ static void ParseMessage(const Message &aMessage, const Core::AddressInfo *aUnic static void SendQuery(const char *aName, uint16_t aRecordType, - uint16_t aRecordClass = ResourceRecord::kClassInternet, - bool aTruncated = false) + uint16_t aRecordClass = ResourceRecord::kClassInternet, + bool aTruncated = false, + bool aLegacyUnicastQuery = false) { Message *message; Header header; @@ -1059,6 +1105,11 @@ static void SendQuery(const char *aName, header.SetType(Header::kTypeQuery); header.SetQuestionCount(1); + if (aLegacyUnicastQuery) + { + header.SetMessageId(kLegacyUnicastMessageId); + } + if (aTruncated) { header.SetTruncationFlag(); @@ -1069,7 +1120,7 @@ static void SendQuery(const char *aName, SuccessOrQuit(message->Append(Question(aRecordType, aRecordClass))); SuccessOrQuit(AsCoreType(&senderAddrInfo.mAddress).FromString(kDeviceIp6Address)); - senderAddrInfo.mPort = kMdnsPort; + senderAddrInfo.mPort = aLegacyUnicastQuery ? kEphemeralPort : kMdnsPort; senderAddrInfo.mInfraIfIndex = 0; Log("Sending query for %s %s", aName, RecordTypeToString(aRecordType)); @@ -1077,7 +1128,11 @@ static void SendQuery(const char *aName, otPlatMdnsHandleReceive(sInstance, message, /* aIsUnicast */ false, &senderAddrInfo); } -static void SendQueryForTwo(const char *aName1, uint16_t aRecordType1, const char *aName2, uint16_t aRecordType2) +static void SendQueryForTwo(const char *aName1, + uint16_t aRecordType1, + const char *aName2, + uint16_t aRecordType2, + bool aIsLegacyUnicast = false) { // Send query with two questions. @@ -1099,7 +1154,7 @@ static void SendQueryForTwo(const char *aName1, uint16_t aRecordType1, const cha SuccessOrQuit(message->Append(Question(aRecordType2, ResourceRecord::kClassInternet))); SuccessOrQuit(AsCoreType(&senderAddrInfo.mAddress).FromString(kDeviceIp6Address)); - senderAddrInfo.mPort = kMdnsPort; + senderAddrInfo.mPort = aIsLegacyUnicast ? kEphemeralPort : kMdnsPort; senderAddrInfo.mInfraIfIndex = 0; Log("Sending query for %s %s and %s %s", aName1, RecordTypeToString(aRecordType1), aName2, @@ -6877,6 +6932,181 @@ void TestPassiveCache(void) testFreeInstance(sInstance); } +void TestLegacyUnicastResponse(void) +{ + Core *mdns = InitTest(); + Core::Host host; + Core::Service service; + const DnsMessage *dnsMsg; + uint16_t heapAllocations; + DnsNameString fullServiceName; + DnsNameString fullServiceType; + DnsNameString hostFullName; + Ip6::Address hostAddresses[2]; + + Log("-------------------------------------------------------------------------------------------"); + Log("TestLegacyUnicastResponse"); + + AdvanceTime(1); + + heapAllocations = sHeapAllocatedPtrs.GetLength(); + SuccessOrQuit(mdns->SetEnabled(true, kInfraIfIndex)); + + SuccessOrQuit(hostAddresses[0].FromString("fd00::1:aaaa")); + SuccessOrQuit(hostAddresses[1].FromString("fd00::1:bbbb")); + host.mHostName = "host"; + host.mAddresses = hostAddresses; + host.mAddressesLength = 2; + host.mTtl = 1500; + hostFullName.Append("%s.local.", host.mHostName); + + service.mHostName = host.mHostName; + service.mServiceInstance = "myservice"; + service.mServiceType = "_srv._udp"; + service.mSubTypeLabels = nullptr; + service.mSubTypeLabelsLength = 0; + service.mTxtData = kTxtData1; + service.mTxtDataLength = sizeof(kTxtData1); + service.mPort = 1234; + service.mPriority = 1; + service.mWeight = 2; + service.mTtl = 1000; + + fullServiceName.Append("%s.%s.local.", service.mServiceInstance, service.mServiceType); + fullServiceType.Append("%s.local.", service.mServiceType); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + + sDnsMessages.Clear(); + + for (RegCallback ®Callbck : sRegCallbacks) + { + regCallbck.Reset(); + } + + SuccessOrQuit(mdns->RegisterHost(host, 0, HandleSuccessCallback)); + SuccessOrQuit(mdns->RegisterService(service, 1, HandleSuccessCallback)); + + AdvanceTime(10 * 1000); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("Send a query with two questions (SRV for service1 and AAAA for host). Validate that no response is sent"); + + AdvanceTime(2000); + + sDnsMessages.Clear(); + SendQueryForTwo(fullServiceName.AsCString(), ResourceRecord::kTypeSrv, hostFullName.AsCString(), + ResourceRecord::kTypeAaaa, /* aIsLegacyUnicast */ true); + + AdvanceTime(200); + + dnsMsg = sDnsMessages.GetHead(); + VerifyOrQuit(dnsMsg == nullptr); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("Send a query for SRV record and validate the response"); + + AdvanceTime(2000); + + sDnsMessages.Clear(); + SendQuery(fullServiceName.AsCString(), ResourceRecord::kTypeSrv, ResourceRecord::kClassInternet, + /* aTruncated */ false, + /* aLegacyUnicastQuery */ true); + + AdvanceTime(1000); + + dnsMsg = sDnsMessages.GetHead(); + VerifyOrQuit(dnsMsg != nullptr); + dnsMsg->ValidateHeader(kLegacyUnicastResponse, /* Q */ 1, /* Ans */ 1, /* Auth */ 0, /* Addnl */ 3); + dnsMsg->Validate(service, kInAnswerSection, kCheckSrv); + dnsMsg->Validate(host, kInAdditionalSection); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("Send a query for TXT record and validate the response"); + + AdvanceTime(2000); + + sDnsMessages.Clear(); + SendQuery(fullServiceName.AsCString(), ResourceRecord::kTypeTxt, ResourceRecord::kClassInternet, + /* aTruncated */ false, + /* aLegacyUnicastQuery */ true); + + AdvanceTime(1000); + + dnsMsg = sDnsMessages.GetHead(); + VerifyOrQuit(dnsMsg != nullptr); + dnsMsg->ValidateHeader(kLegacyUnicastResponse, /* Q */ 1, /* Ans */ 1, /* Auth */ 0, /* Addnl */ 1); + dnsMsg->Validate(service, kInAnswerSection, kCheckTxt); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("Send a query for ANY record and validate the response"); + + AdvanceTime(2000); + + sDnsMessages.Clear(); + SendQuery(fullServiceName.AsCString(), ResourceRecord::kTypeAny, ResourceRecord::kClassInternet, + /* aTruncated */ false, + /* aLegacyUnicastQuery */ true); + + AdvanceTime(1000); + + dnsMsg = sDnsMessages.GetHead(); + VerifyOrQuit(dnsMsg != nullptr); + dnsMsg->ValidateHeader(kLegacyUnicastResponse, /* Q */ 1, /* Ans */ 2, /* Auth */ 0, /* Addnl */ 3); + dnsMsg->Validate(service, kInAnswerSection, kCheckSrv | kCheckTxt); + dnsMsg->Validate(host, kInAdditionalSection); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("Send a query for PTR record for service type and validate the response"); + + AdvanceTime(2000); + + sDnsMessages.Clear(); + SendQuery(fullServiceType.AsCString(), ResourceRecord::kTypePtr, ResourceRecord::kClassInternet, + /* aTruncated */ false, + /* aLegacyUnicastQuery */ true); + + AdvanceTime(1000); + + dnsMsg = sDnsMessages.GetHead(); + VerifyOrQuit(dnsMsg != nullptr); + dnsMsg->ValidateHeader(kLegacyUnicastResponse, /* Q */ 1, /* Ans */ 1, /* Auth */ 0, /* Addnl */ 4); + dnsMsg->Validate(service, kInAnswerSection, kCheckPtr); + dnsMsg->Validate(service, kInAdditionalSection, kCheckSrv | kCheckTxt); + dnsMsg->Validate(host, kInAdditionalSection); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("Send a query for non-existing record and validate the response with NSEC"); + + AdvanceTime(2000); + + sDnsMessages.Clear(); + SendQuery(hostFullName.AsCString(), ResourceRecord::kTypeA, ResourceRecord::kClassInternet, /* aTruncated */ false, + /* aLegacyUnicastQuery */ true); + + AdvanceTime(1000); + + dnsMsg = sDnsMessages.GetHead(); + VerifyOrQuit(dnsMsg != nullptr); + dnsMsg->ValidateHeader(kLegacyUnicastResponse, /* Q */ 1, /* Ans */ 0, /* Auth */ 0, /* Addnl */ 1); + VerifyOrQuit(dnsMsg->mAdditionalRecords.ContainsNsec(hostFullName, ResourceRecord::kTypeAaaa)); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + + sDnsMessages.Clear(); + + SuccessOrQuit(mdns->UnregisterHost(host)); + + AdvanceTime(15000); + + SuccessOrQuit(mdns->SetEnabled(false, kInfraIfIndex)); + VerifyOrQuit(sHeapAllocatedPtrs.GetLength() <= heapAllocations); + + Log("End of test"); + + testFreeInstance(sInstance); +} + } // namespace Multicast } // namespace Dns } // namespace ot @@ -6904,6 +7134,7 @@ int main(void) ot::Dns::Multicast::TestTxtResolver(); ot::Dns::Multicast::TestIp6AddrResolver(); ot::Dns::Multicast::TestPassiveCache(); + ot::Dns::Multicast::TestLegacyUnicastResponse(); printf("All tests passed\n"); #else diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index 78a608965..89dfda775 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -99,6 +99,9 @@ void testFreeInstance(otInstance *aInstance) bool sDiagMode = false; +static otPlatDiagOutputCallback sOutputCallback = nullptr; +static void *sOutputCallbackContext = nullptr; + extern "C" { #if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE @@ -225,9 +228,30 @@ OT_TOOL_WEAK otError otPlatEntropyGet(uint8_t *aOutput, uint16_t aOutputLength) return error; } -OT_TOOL_WEAK void otPlatDiagProcess(otInstance *, uint8_t, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +static void DiagOutput(const char *aFormat, ...) +{ + va_list args; + + va_start(args, aFormat); + + if (sOutputCallback != nullptr) + { + sOutputCallback(aFormat, args, sOutputCallbackContext); + } + + va_end(args); +} + +OT_TOOL_WEAK void otPlatDiagSetOutputCallback(otInstance *aInstance, otPlatDiagOutputCallback aCallback, void *aContext) { - snprintf(aOutput, aOutputMaxLen, "diag feature '%s' is not supported\r\n", aArgs[0]); + sOutputCallback = aCallback; + sOutputCallbackContext = aContext; +} + +OT_TOOL_WEAK otError otPlatDiagProcess(otInstance *, uint8_t, char *aArgs[]) +{ + DiagOutput("diag feature '%s' is not supported\r\n", aArgs[0]); + return OT_ERROR_NONE; } OT_TOOL_WEAK void otPlatDiagModeSet(bool aMode) { sDiagMode = aMode; } @@ -877,6 +901,66 @@ OT_TOOL_WEAK void otPlatDnssdUnregisterKey(otInstance *aInstance OT_UNUSED_VARIABLE(aCallback); } +OT_TOOL_WEAK void otPlatDnssdStartBrowser(otInstance *aInstance, const otPlatDnssdBrowser *aBrowser) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aBrowser); +} + +OT_TOOL_WEAK void otPlatDnssdStopBrowser(otInstance *aInstance, const otPlatDnssdBrowser *aBrowser) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aBrowser); +} + +OT_TOOL_WEAK void otPlatDnssdStartSrvResolver(otInstance *aInstance, const otPlatDnssdSrvResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + +OT_TOOL_WEAK void otPlatDnssdStopSrvResolver(otInstance *aInstance, const otPlatDnssdSrvResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + +OT_TOOL_WEAK void otPlatDnssdStartTxtResolver(otInstance *aInstance, const otPlatDnssdTxtResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + +OT_TOOL_WEAK void otPlatDnssdStopTxtResolver(otInstance *aInstance, const otPlatDnssdTxtResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + +OT_TOOL_WEAK void otPlatDnssdStartIp6AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + +OT_TOOL_WEAK void otPlatDnssdStopIp6AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + +OT_TOOL_WEAK void otPlatDnssdStartIp4AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + +OT_TOOL_WEAK void otPlatDnssdStopIp4AddressResolver(otInstance *aInstance, const otPlatDnssdAddressResolver *aResolver) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aResolver); +} + #endif // OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE #if OPENTHREAD_CONFIG_PLATFORM_LOG_CRASH_DUMP_ENABLE diff --git a/tests/unit/test_platform.h b/tests/unit/test_platform.h index 8f9d2917a..66df45a5b 100644 --- a/tests/unit/test_platform.h +++ b/tests/unit/test_platform.h @@ -33,6 +33,7 @@ #include #include +#include #include #include #include diff --git a/tests/unit/test_srp_server.cpp b/tests/unit/test_srp_server.cpp index 2104ae827..f0218e004 100644 --- a/tests/unit/test_srp_server.cpp +++ b/tests/unit/test_srp_server.cpp @@ -1024,6 +1024,187 @@ void TestUpdateLeaseShortVariant(void) Log("End of TestUpdateLeaseShortVariant"); } +static uint16_t sServerRxCount; +static Ip6::MessageInfo sServerMsgInfo; +static uint16_t sServerLastMsgId; + +void HandleServerUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +{ + Dns::Header header; + + VerifyOrQuit(aContext == nullptr); + VerifyOrQuit(aMessage != nullptr); + VerifyOrQuit(aMessageInfo != nullptr); + + SuccessOrQuit(AsCoreType(aMessage).Read(0, header)); + + sServerMsgInfo = AsCoreType(aMessageInfo); + sServerLastMsgId = header.GetMessageId(); + sServerRxCount++; + + Log("HandleServerUdpReceive(), message-id: 0x%x", header.GetMessageId()); +} + +void TestSrpClientDelayedResponse(void) +{ + static constexpr uint16_t kServerPort = 53535; + + Srp::Client *srpClient; + Srp::Client::Service service1; + Srp::Client::Service service2; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestSrpClientDelayedResponse"); + + InitTest(); + + srpClient = &sInstance->Get(); + + for (uint8_t testIter = 0; testIter < 3; testIter++) + { + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); + Log("testIter = %u", testIter); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Prepare a socket to act as SRP server. + + Ip6::Udp::Socket udpSocket(*sInstance, HandleServerUdpReceive, nullptr); + Ip6::SockAddr serverSockAddr; + uint16_t firstMsgId; + Message *response; + Dns::UpdateHeader header; + + sServerRxCount = 0; + + SuccessOrQuit(udpSocket.Open()); + SuccessOrQuit(udpSocket.Bind(kServerPort, Ip6::kNetifThread)); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Manually start the client with a message ID based on `testIter` + // We use zero in the first iteration, `0xffff` in the second + // iteration to test wrapping of 16-bit message ID. + + switch (testIter) + { + case 0: + srpClient->SetNextMessageId(0); + break; + case 1: + srpClient->SetNextMessageId(0xffff); + break; + case 2: + srpClient->SetNextMessageId(0xaaaa); + break; + } + + serverSockAddr.SetAddress(sInstance->Get().GetMeshLocalRloc()); + serverSockAddr.SetPort(kServerPort); + SuccessOrQuit(srpClient->Start(serverSockAddr)); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register a service + + SuccessOrQuit(srpClient->SetHostName(kHostName)); + SuccessOrQuit(srpClient->EnableAutoHostAddress()); + + PrepareService1(service1); + SuccessOrQuit(srpClient->AddService(service1)); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Wait for short time and make sure server receives an SRP + // update message from client. + + AdvanceTime(1 * 1000); + + VerifyOrQuit(sServerRxCount == 1); + firstMsgId = sServerLastMsgId; + + switch (testIter) + { + case 0: + VerifyOrQuit(firstMsgId == 0); + break; + case 1: + VerifyOrQuit(firstMsgId == 0xffff); + break; + case 2: + VerifyOrQuit(firstMsgId == 0xaaaa); + break; + } + + if (testIter == 2) + { + AdvanceTime(2 * 1000); + + PrepareService2(service2); + SuccessOrQuit(srpClient->AddService(service2)); + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Wait for longer to allow client to retry a bunch of times + + AdvanceTime(20 * 1000); + VerifyOrQuit(sServerRxCount > 1); + VerifyOrQuit(sServerLastMsgId != firstMsgId); + + VerifyOrQuit(srpClient->GetHostInfo().GetState() != Srp::Client::kRegistered); + VerifyOrQuit(service1.GetState() != Srp::Client::kRegistered); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Now send a delayed response from server using the first + // message ID. + + response = udpSocket.NewMessage(); + VerifyOrQuit(response != nullptr); + + Log("Sending response with msg-id: 0x%x", firstMsgId); + + header.SetMessageId(firstMsgId); + header.SetType(Dns::UpdateHeader::kTypeResponse); + header.SetResponseCode(Dns::UpdateHeader::kResponseSuccess); + SuccessOrQuit(response->Append(header)); + SuccessOrQuit(udpSocket.SendTo(*response, sServerMsgInfo)); + + AdvanceTime(10); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // In the first two iterations, we ensure that client + // did successfully accept the response with older message ID. + // This should not be the case in the third iteration due to + // changes to client services after first UPdate message was + // sent by client. + + switch (testIter) + { + case 0: + case 1: + VerifyOrQuit(srpClient->GetHostInfo().GetState() == Srp::Client::kRegistered); + VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered); + break; + case 2: + VerifyOrQuit(srpClient->GetHostInfo().GetState() != Srp::Client::kRegistered); + VerifyOrQuit(service1.GetState() != Srp::Client::kRegistered); + break; + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Remove service and close socket. + + srpClient->ClearHostAndServices(); + srpClient->Stop(); + + SuccessOrQuit(udpSocket.Close()); + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance + + Log("Finalizing OT instance"); + FinalizeTest(); + + Log("End of TestSrpClientDelayedResponse"); +} + #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE #endif // ENABLE_SRP_TEST @@ -1040,7 +1221,9 @@ int main(void) ot::TestSrpServerClientRemove(/* aShouldRemoveKeyLease */ false); #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE ot::TestUpdateLeaseShortVariant(); + ot::TestSrpClientDelayedResponse(); #endif + printf("All tests passed\n"); #else printf("SRP_SERVER or SRP_CLIENT feature is not enabled\n"); diff --git a/tools/otci/otci/__init__.py b/tools/otci/otci/__init__.py index 7cb0aa45b..eed56c615 100644 --- a/tools/otci/otci/__init__.py +++ b/tools/otci/otci/__init__.py @@ -37,7 +37,8 @@ connect_ncp_sim, \ connect_cmd_handler, \ connect_otbr_ssh, \ - connect_otbr_adb + connect_otbr_adb_tcp, \ + connect_otbr_adb_usb from .types import Rloc16, ChildId, NetifIdentifier @@ -46,7 +47,8 @@ 'connect_cli_serial', 'connect_ncp_sim', 'connect_otbr_ssh', - 'connect_otbr_adb', + 'connect_otbr_adb_tcp', + 'connect_otbr_adb_usb', 'connect_cmd_handler', ] diff --git a/tools/otci/otci/command_handlers.py b/tools/otci/otci/command_handlers.py index 7c790e6d8..ed08584c6 100644 --- a/tools/otci/otci/command_handlers.py +++ b/tools/otci/otci/command_handlers.py @@ -105,8 +105,7 @@ def __init__(self, otcli: OtCliHandler, is_spinel_cli=False): self.__pending_lines = queue.Queue() self.__should_close = threading.Event() - self.__otcli_reader = threading.Thread(target=self.__otcli_read_routine) - self.__otcli_reader.setDaemon(True) + self.__otcli_reader = threading.Thread(target=self.__otcli_read_routine, daemon=True) self.__otcli_reader.start() def __repr__(self): @@ -285,19 +284,13 @@ def set_line_read_callback(self, callback: Optional[Callable[[str], Any]]): class OtbrAdbCommandRunner(OTCommandHandler): - def __init__(self, host, port): - from adb_shell.adb_device import AdbDeviceTcp - - self.__host = host - self.__port = port - self.__adb = AdbDeviceTcp(host, port, default_transport_timeout_s=9.0) + from adb_shell.adb_device import AdbDevice + def __init__(self, adb: AdbDevice): + self.__adb = adb self.__line_read_callback = None self.__adb.connect(rsa_keys=None, auth_timeout_s=0.1) - def __repr__(self): - return f'{self.__host}:{self.__port}' - def execute_command(self, cmd: str, timeout: float) -> List[str]: sh_cmd = f'ot-ctl {cmd}' @@ -324,3 +317,32 @@ def wait(self, duration: float) -> List[str]: def set_line_read_callback(self, callback: Optional[Callable[[str], Any]]): self.__line_read_callback = callback + + +class OtbrAdbTcpCommandRunner(OtbrAdbCommandRunner): + + def __init__(self, host: str, port: int): + from adb_shell.adb_device import AdbDeviceTcp + + self.__host = host + self.__port = port + + adb = AdbDeviceTcp(host, port, default_transport_timeout_s=9.0) + super(OtbrAdbTcpCommandRunner, self).__init__(adb) + + def __repr__(self): + return f'{self.__host}:{self.__port}' + + +class OtbrAdbUsbCommandRunner(OtbrAdbCommandRunner): + + def __init__(self, serial: str): + from adb_shell.adb_device import AdbDeviceUsb + + self.__serial = serial + + adb = AdbDeviceUsb(serial, port_path=None, default_transport_timeout_s=9.0) + super(OtbrAdbUsbCommandRunner, self).__init__(adb) + + def __repr__(self): + return f'USB:{self.__serial}' diff --git a/tools/otci/otci/otci.py b/tools/otci/otci/otci.py index e60acf8ee..93bb56a67 100644 --- a/tools/otci/otci/otci.py +++ b/tools/otci/otci/otci.py @@ -33,7 +33,7 @@ from typing import Callable, List, Collection, Union, Tuple, Optional, Dict, Pattern, Any from . import connectors -from .command_handlers import OTCommandHandler, OtCliCommandRunner, OtbrSshCommandRunner, OtbrAdbCommandRunner +from .command_handlers import OTCommandHandler, OtCliCommandRunner, OtbrSshCommandRunner, OtbrAdbTcpCommandRunner, OtbrAdbUsbCommandRunner from .connectors import Simulator from .errors import UnexpectedCommandOutput, ExpectLineTimeoutError, CommandError, InvalidArgumentsError from .types import ChildId, Rloc16, Ip6Addr, ThreadState, PartitionId, DeviceMode, RouterId, SecurityPolicy, Ip6Prefix, \ @@ -1946,18 +1946,6 @@ def has_ipmaddr(self, ip: Union[str, ipaddress.IPv6Address]): """Check if a IPv6 multicast address was subscribed by the Thread interface.""" return ip in self.get_ipmaddrs() - def get_ipmaddr_promiscuous(self) -> bool: - """Get multicast promiscuous mode.""" - return self.__parse_Enabled_or_Disabled(self.execute_command("ipmaddr promiscuous")) - - def enable_ipmaddr_promiscuous(self): - """Enable multicast promiscuous mode.""" - self.execute_command('ipmaddr promiscuous enable') - - def disable_ipmaddr_promiscuous(self): - """Disable multicast promiscuous mode.""" - self.execute_command('ipmaddr promiscuous disable') - def get_ipmaddr_llatn(self) -> Ip6Addr: """Get Link Local All Thread Nodes Multicast Address""" return self.__parse_ip6addr(self.execute_command('ipmaddr llatn')) @@ -2515,8 +2503,13 @@ def connect_otbr_ssh(host: str, port: int = 22, username='pi', password='raspber return OTCI(cmd_handler) -def connect_otbr_adb(host: str, port: int = 5555): - cmd_handler = OtbrAdbCommandRunner(host, port) +def connect_otbr_adb_tcp(host: str, port: int = 5555): + cmd_handler = OtbrAdbTcpCommandRunner(host, port) + return OTCI(cmd_handler) + + +def connect_otbr_adb_usb(serial: str): + cmd_handler = OtbrAdbUsbCommandRunner(serial) return OTCI(cmd_handler) diff --git a/tools/otci/tests/test_otci.py b/tools/otci/tests/test_otci.py index 011c8a379..d065867aa 100644 --- a/tools/otci/tests/test_otci.py +++ b/tools/otci/tests/test_otci.py @@ -257,12 +257,6 @@ def _test_otci_single_node(self, leader): logging.info("EID-to-RLOC cache: %r", leader.get_eidcache()) - logging.info("ipmaddr promiscuous: %r", leader.get_ipmaddr_promiscuous()) - leader.enable_ipmaddr_promiscuous() - self.assertTrue(leader.get_ipmaddr_promiscuous()) - leader.disable_ipmaddr_promiscuous() - self.assertFalse(leader.get_ipmaddr_promiscuous()) - logging.info("leader data: %r", leader.get_leader_data()) logging.info("leader neighbor list: %r", leader.get_neighbor_list()) logging.info("leader neighbor table: %r", leader.get_neighbor_table())