[Github Sync] Merge github/main to aosp/main
Bug: 333301206
* github/main:
[mle] MTD to handle udp time sync message (#9978)
[plat-utils] enhance `otMacFrameDoesAddrMatch()` (#9997)
[netdata-publisher] distinguish SRP/DNS unicast entries (#9937)
[logging] introduce `LogWarnOnError()` for standardized error logging (#9996)
[test] add tests for multi-BR network resilience during BR removal (#9990)
[srp-sever] add `LogError()` (#9992)
[dataset] define `Dataset::Tlvs` type (#9991)
[ip6] allow delivering RLOC/ALOC traffic to host (#9987)
[mle] simplify `CheckReachablity()` (#9989)
[srp-server] retry other ports when failing to `prepareSocket` (#9981)
[link-metrics] fix race condition (#9986)
[doc] use space char instead of tabs in `ot_config_doc.h` (#9983)
github-actions: bump actions/checkout from 4.1.1 to 4.1.2 (#9982)
[mesh-forwarder] update `CheckReachablity()` to handle malformed messages (#9976)
[netata] simplify `RemoveTemporaryData()` (#9975)
[cli] `netdata show` to support filtering by RLOC16 (#9969)
[mle] improve MLE module comments (#9972)
[cli] fix CoAP response code for POST 2.03 -> 2.04 per RFC 7252 (#9971)
[mle] include child RLOC16 in the logs from `HandleTimeTick()` (#9968)
[netdata] refactor `MutableNetworkData` methods for code organization (#9970)
[routing-manager] set extra options to append to emitted RAs (#9945)
[test] add `tcat` unit tests (#9953)
[cli] add `OutputNetworkData()` (#9965)
[routing-manager] employ RA hash tracking to detect self-originating RAs (#9939)
[channel-manager] add local csl channel selection on SSED (#9641)
[netdata] add `FindRlocs()` to retrieve RLOC16 of entries (#9961)
[version] introduce `OT_THREAD_VERSION_1_4` (#9946)
github-actions: bump actions/download-artifact from 4.1.1 to 4.1.4 (#9963)
[config] update docs for `SED_BUFFER_SIZE` & `SED_DATAGRAM_COUNT` (#9962)
[spinel] extract log module from radio spinel (#9957)
[mle] simplify `HandleTimeTick()` code related to aging routers (#9955)
[mle] retain direct child cache entries on Addr Solicit Response TX (#9956)
[test] simplify `test_pskc` unit test (#9952)
[cli] move `Process{Get/Set/Enable/Disable}` methods to Utils class (#9951)
[simulation] add simulation tests framework for tcat (#9724)
[coap] fix copying option (#9894)
github-actions: bump docker/setup-buildx-action from 3.0.0 to 3.2.0 (#9941)
[routing-manager] simplify `Ip6::Nd` type usage (#9938)
[thread-cert] use `unittest` assert methods (#9936)
[lib] include `(void)` in function prototype in `reset_util.h` (#9934)
[routing-manager] update use of `Ip6::Nd::Option` enumerator (#9931)
[posix] remove use of core-internal types in `netif.cpp` platform (#9928)
[test] enhance `test_netdata_publisher` robustness (#9932)
[thread-cert] use `ROUTER_STARTUP_DELAY` in `test_detach` (#9933)
[mle] disable MLE retransmissions when detaching (#9929)
[simulation] allow specify local host (#9925)
[routing-manager] construct RA dynamically using `Heap::Array` (#9924)
[posix] ensure module names are included in platform logs (#9920)
[tests] fix `test_manual_maddress.py` (#9923)
[thread-cert] set device mode in `test_mle_msg_key_seq_jump` (#9921)
[posix] add IPv6 address helper functions in `Ip6Utils` (#9917)
[daemon] always initialize the OT CLI when setting up the daemon (#9919)
[misc] remove stale forward declaration (#9911)
github-actions: bump actions/upload-artifact from 4.2.0 to 4.3.1 (#9914)
[docs] add documentation for CLI `dataset updater` command (#9905)
[test] add `test_key_rotation_and_key_guard_time` (#9906)
[srp-server] add snoop cache entries for registered host addresses (#9881)
[key-manager] update how key guard time is determined and applied (#9871)
[toranj-config] disable `OT_BORDER_ROUTING` on NCP builds (#9907)
[simulation] implement `otPlatInfraIf` APIs in simulation platform (#9895)
[posix] add otSysCliInitUsingDaemon API (#9897)
[routing-manager] delay sending RAs until after initial policy evaluation (#9896)
[posix] add missing header guards, style fixes (#9892)
[border-agent] mechanism to use ephemeral key (#9435)
[posix] enable building `infra_if.cpp` on macOS (#9891)
[string] return `0` if `nullptr` is passed to `StringLength` (#9886)
[posix] fix member var access from static `CreateIcmp6Socket()` (#9887)
github-actions: bump github/codeql-action from 3.23.2 to 3.24.6 (#9890)
github-actions: bump codecov/codecov-action from 3.1.4 to 4.0.2 (#9877)
[cli] add documentation for `nexthop` command in `README.md` (#9882)
[simulation] add `simul_utils.h` for socket operation helpers (#9879)
[message] track received `ThreadLinkInfo` in `Message` metadata (#9878)
[spinel] drop received frames between RCP disable and reset (#9793)
[link-quality] prevent overflow in `LqiAverager` calculation (#9876)
[cli] add `@moreinfo` Doxygen tags to reference CoAPS Concepts Guide (#9867)
[core] use `NumericLimits<>` constants (#9875)
Change-Id: I65a1c577c22335916c3ba68f164a76b6d1409831
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 007430e..953788e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -53,7 +53,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -75,7 +75,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368 # v1
with:
use-verbose-mode: 'yes'
@@ -89,7 +89,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -108,7 +108,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -148,7 +148,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -167,7 +167,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -186,14 +186,14 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
run: |
sudo apt-get --no-install-recommends install -y ninja-build libreadline-dev libncurses-dev
rm -rf third_party/mbedtls/repo
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
repository: ARMmbed/mbedtls
ref: v3.5.0
@@ -242,7 +242,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -283,7 +283,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -316,7 +316,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -354,7 +354,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -382,7 +382,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -418,7 +418,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -442,7 +442,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Install unzip
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 158892f..cc1f543 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -59,14 +59,14 @@
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- name: Checkout repository
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- name: Bootstrap
run: |
sudo apt-get --no-install-recommends install -y ninja-build libreadline-dev libncurses-dev
- name: Initialize CodeQL
- uses: github/codeql-action/init@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v3.23.2
+ uses: github/codeql-action/init@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -80,6 +80,6 @@
./script/test build
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v3.23.2
+ uses: github/codeql-action/analyze@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
with:
category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index c336a2b..9738b85 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -59,7 +59,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
@@ -83,7 +83,7 @@
${TAGS} --file ${DOCKER_FILE} ." >> $GITHUB_OUTPUT
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
+ uses: docker/setup-buildx-action@2b51285047da1547ffb1b2203d8be4c0af6b1f20 # v3.2.0
- name: Docker Buildx (build)
run: |
diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml
index 97cb4b5..da612bb 100644
--- a/.github/workflows/fuzz.yml
+++ b/.github/workflows/fuzz.yml
@@ -61,7 +61,7 @@
fuzz-seconds: 1800
dry-run: false
- name: Upload Crash
- uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: failure()
with:
name: artifacts
diff --git a/.github/workflows/makefile-check.yml b/.github/workflows/makefile-check.yml
index 83bce98..1d7fa55 100644
--- a/.github/workflows/makefile-check.yml
+++ b/.github/workflows/makefile-check.yml
@@ -52,7 +52,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Check
diff --git a/.github/workflows/otbr.yml b/.github/workflows/otbr.yml
index 4ba7ea6..ac04744 100644
--- a/.github/workflows/otbr.yml
+++ b/.github/workflows/otbr.yml
@@ -62,7 +62,7 @@
# of OMR prefix and Domain prefix is not deterministic.
BORDER_ROUTING: 0
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Build OTBR Docker
@@ -86,12 +86,12 @@
export CI_ENV="$(bash <(curl -s https://codecov.io/env)) -e GITHUB_ACTIONS -e COVERAGE"
echo "CI_ENV=${CI_ENV}"
sudo -E ./script/test cert_suite ./tests/scripts/thread-cert/backbone/*.py || (sudo chmod a+r *.log *.json *.pcap && false)
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-thread-1-3-backbone-docker
path: /tmp/coverage/
retention-days: 1
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: thread-1-3-backbone-results
@@ -104,7 +104,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-thread-1-3-backbone
path: tmp/coverage.info
@@ -181,7 +181,7 @@
NAT64: ${{ matrix.nat64 }}
MAX_JOBS: 3
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- name: Set firewall environment variables
if: ${{ matrix.use_core_firewall }}
run: |
@@ -208,12 +208,12 @@
export CI_ENV="$(bash <(curl -s https://codecov.io/env)) -e GITHUB_ACTIONS -e COVERAGE"
echo "CI_ENV=${CI_ENV}"
sudo -E ./script/test cert_suite ${{ matrix.cert_scripts }} || (sudo chmod a+r *.log *.json *.pcap && false)
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-br-docker-${{ matrix.description }}-${{ matrix.otbr_mdns }}-${{matrix.otbr_trel}}
path: /tmp/coverage/
retention-days: 1
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: br-results-${{ matrix.description }}-${{ matrix.otbr_mdns }}-${{matrix.otbr_trel}}
@@ -226,7 +226,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-br-${{ matrix.description }}-${{ matrix.otbr_mdns }}-${{matrix.otbr_trel}}
path: tmp/coverage.info
@@ -238,13 +238,13 @@
- thread-border-router
runs-on: ubuntu-20.04
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
run: |
sudo apt-get --no-install-recommends install -y lcov
- - uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
+ - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with:
path: coverage/
pattern: cov-*
@@ -255,7 +255,7 @@
script/test combine_coverage
- name: Upload Coverage
continue-on-error: true
- uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
+ uses: codecov/codecov-action@0cfda1dd0a4ad9efc75517f399d859cd1ea4ced1 # v4.0.2
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
diff --git a/.github/workflows/otci.yml b/.github/workflows/otci.yml
index 37a0149..d22c227 100644
--- a/.github/workflows/otci.yml
+++ b/.github/workflows/otci.yml
@@ -61,7 +61,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- name: Bootstrap
run: |
sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
diff --git a/.github/workflows/otns.yml b/.github/workflows/otns.yml
index 4b46095..7abbc91 100644
--- a/.github/workflows/otns.yml
+++ b/.github/workflows/otns.yml
@@ -62,7 +62,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
with:
go-version: "1.20"
@@ -82,7 +82,7 @@
cd /tmp/otns
./script/test py-unittests
)
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: unittests-pcaps
@@ -92,7 +92,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-otns-unittests
path: tmp/coverage.info
@@ -102,7 +102,7 @@
name: Examples
runs-on: ubuntu-22.04
steps:
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
with:
go-version: "1.20"
@@ -122,7 +122,7 @@
cd /tmp/otns
./script/test py-examples
)
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: examples-pcaps
@@ -132,7 +132,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-otns-examples
path: tmp/coverage.info
@@ -164,7 +164,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
with:
go-version: "1.20"
@@ -184,7 +184,7 @@
cd /tmp/otns
./script/test stress-tests ${{ matrix.suite }}
)
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: stress-tests-${{ matrix.suite }}-pcaps
@@ -194,7 +194,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-otns-stress-tests-${{ matrix.suite }}
path: tmp/coverage.info
@@ -212,11 +212,11 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- name: Bootstrap
run: |
sudo apt-get --no-install-recommends install -y lcov
- - uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
+ - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with:
path: coverage/
pattern: cov-*
diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml
index 7ab0ef2..fd8e7d8 100644
--- a/.github/workflows/posix.yml
+++ b/.github/workflows/posix.yml
@@ -56,10 +56,11 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- name: Bootstrap
run: |
sudo apt-get --no-install-recommends install -y expect ninja-build lcov socat
+ pip install bleak
- name: Run RCP Mode
run: |
ulimit -c unlimited
@@ -75,7 +76,7 @@
CRASHED=$(./script/test check_crash | tail -1)
[[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
echo "CRASHED_RCP=$CRASHED" >> $GITHUB_ENV
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() && env.CRASHED_RCP == '1' }}
with:
name: core-expect-rcp
@@ -84,7 +85,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-expects-linux-1
path: tmp/coverage.info
@@ -108,13 +109,13 @@
CRASHED=$(./script/test check_crash | tail -1)
[[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
echo "CRASHED_TUN=$CRASHED" >> $GITHUB_ENV
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() && env.CRASHED_TUN == '1' }}
with:
name: core-expect-linux
path: |
./ot-core-dump/*
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: syslog-expect-linux
@@ -122,7 +123,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-expects-linux-2
path: tmp/coverage.info
@@ -141,7 +142,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -155,7 +156,7 @@
- name: Run
run: |
MAX_JOBS=$(getconf _NPROCESSORS_ONLN) ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: thread-cert
@@ -163,7 +164,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-thread-cert
path: tmp/coverage.info
@@ -185,7 +186,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- name: Bootstrap
run: |
sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
@@ -213,7 +214,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-pty-linux-${{ matrix.OT_DAEMON }}
path: tmp/coverage.info
@@ -235,7 +236,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- name: Bootstrap
run: |
rm -f /usr/local/bin/2to3
@@ -265,7 +266,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- name: Bootstrap
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
@@ -281,7 +282,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-rcp-stack-reset
path: tmp/coverage.info
@@ -299,13 +300,13 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
run: |
sudo apt-get --no-install-recommends install -y lcov
- - uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
+ - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with:
path: coverage/
pattern: cov-*
@@ -314,7 +315,7 @@
run: |
script/test combine_coverage
- name: Upload Coverage
- uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
+ uses: codecov/codecov-action@0cfda1dd0a4ad9efc75517f399d859cd1ea4ced1 # v4.0.2
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 5fd261b..a9a13cc 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -60,7 +60,7 @@
steps:
- name: "Checkout code"
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
persist-credentials: false
@@ -87,7 +87,7 @@
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
- uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v3.1.0
+ uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v3.1.0
with:
name: SARIF file
path: results.sarif
@@ -95,6 +95,6 @@
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
- uses: github/codeql-action/upload-sarif@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v2.1.27
+ uses: github/codeql-action/upload-sarif@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v2.1.27
with:
sarif_file: results.sarif
diff --git a/.github/workflows/simulation-1.1.yml b/.github/workflows/simulation-1.1.yml
index 9f64444..ee08f39 100644
--- a/.github/workflows/simulation-1.1.yml
+++ b/.github/workflows/simulation-1.1.yml
@@ -59,7 +59,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -76,7 +76,7 @@
- name: Run
run: |
./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: packet-verification-pcaps
@@ -86,7 +86,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-packet-verification
path: tmp/coverage.info
@@ -108,7 +108,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -122,7 +122,7 @@
- name: Run
run: |
./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: cli-ftd-thread-cert
@@ -130,7 +130,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-cli-ftd
path: tmp/coverage.info
@@ -159,7 +159,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -173,7 +173,7 @@
- name: Run
run: |
./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: cli-mtd-thread-cert
@@ -181,7 +181,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-cli-mtd-${{ matrix.message_use_heap }}
path: tmp/coverage.info
@@ -203,7 +203,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -217,7 +217,7 @@
- name: Run
run: |
./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: cli-time-sync-thread-cert
@@ -225,7 +225,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-cli-time-sync
path: tmp/coverage.info
@@ -243,10 +243,11 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- name: Bootstrap
run: |
sudo apt-get --no-install-recommends install -y expect ninja-build lcov socat
+ pip install bleak
- name: Run
run: |
ulimit -c unlimited
@@ -258,7 +259,7 @@
CRASHED=$(./script/test check_crash | tail -1)
[[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
echo "CRASHED_CLI=$CRASHED" >> $GITHUB_ENV
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() && env.CRASHED_CLI == '1' }}
with:
name: core-expect-cli
@@ -267,7 +268,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-expects
path: tmp/coverage.info
@@ -283,7 +284,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -317,7 +318,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-ot-commissioner
path: tmp/coverage.info
@@ -336,7 +337,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -349,7 +350,7 @@
- name: Run
run: |
./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: ot_testing
@@ -357,7 +358,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-multiple-instance
path: tmp/coverage.info
@@ -379,13 +380,13 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
run: |
sudo apt-get --no-install-recommends install -y lcov
- - uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
+ - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with:
path: coverage/
pattern: cov-*
@@ -394,7 +395,7 @@
run: |
script/test combine_coverage
- name: Upload Coverage
- uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
+ uses: codecov/codecov-action@0cfda1dd0a4ad9efc75517f399d859cd1ea4ced1 # v4.0.2
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.2.yml
index 35a0f0d..26153f4 100644
--- a/.github/workflows/simulation-1.2.yml
+++ b/.github/workflows/simulation-1.2.yml
@@ -70,7 +70,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -95,12 +95,12 @@
CRASHED=$(./script/test check_crash | tail -1)
[[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
echo "CRASHED=$CRASHED" >> $GITHUB_ENV
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }}-pcaps
path: "*.pcap"
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() && env.CRASHED == '1' }}
with:
name: core-packet-verification-thread-1-3
@@ -109,7 +109,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage "${{ matrix.compiler.gcov }}"
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }}
path: tmp/coverage.info
@@ -132,7 +132,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -166,14 +166,14 @@
CRASHED=$(./script/test check_crash | tail -1)
[[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
echo "CRASHED=$CRASHED" >> $GITHUB_ENV
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: packet-verification-low-power-pcaps
path: |
*.pcap
*.json
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() && env.CRASHED == '1' }}
with:
name: core-packet-verification-low-power
@@ -182,7 +182,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-packet-verification-low-power
path: tmp/coverage.info
@@ -203,7 +203,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -220,7 +220,7 @@
- name: Run
run: |
./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: packet-verification-1.1-on-1.3-pcaps
@@ -230,12 +230,62 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-packet-verification-1-1-on-1-3
path: tmp/coverage.info
retention-days: 1
+ channel-manager-csl:
+ runs-on: ubuntu-20.04
+ env:
+ CFLAGS: -m32
+ CXXFLAGS: -m32
+ LDFLAGS: -m32
+ COVERAGE: 1
+ THREAD_VERSION: 1.3
+ VIRTUAL_TIME: 1
+ INTER_OP: 1
+ INTER_OP_BBR: 1
+ ADDON_FEAT_1_2: 1
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
+ with:
+ egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
+
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
+ with:
+ submodules: true
+ - name: Bootstrap
+ run: |
+ sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
+ sudo apt-get --no-install-recommends install -y g++-multilib lcov ninja-build python3-setuptools python3-wheel
+ python3 -m pip install -r tests/scripts/thread-cert/requirements.txt
+ - name: Build
+ run: |
+ OT_OPTIONS="-DOT_CHANNEL_MANAGER_CSL=ON" ./script/test build
+ - name: Run
+ run: |
+ ulimit -c unlimited
+ ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py
+ ./script/test cert_suite ./tests/scripts/thread-cert/test_*.py
+ ./script/test cert_suite ./tests/scripts/thread-cert/v1_2_*.py
+ ./script/test cert_suite ./tests/scripts/thread-cert/addon_test_channel_manager_autocsl*.py
+ - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ if: ${{ failure() }}
+ with:
+ name: channel-manager-csl
+ path: ot_testing
+ - name: Generate Coverage
+ run: |
+ ./script/test generate_coverage gcc
+ - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ with:
+ name: cov-channel-manager-csl
+ path: tmp/coverage.info
+ retention-days: 1
+
expects:
runs-on: ubuntu-20.04
env:
@@ -248,12 +298,13 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
run: |
sudo apt-get --no-install-recommends install -y expect ninja-build lcov socat
+ pip install bleak
- name: Run RCP Mode
run: |
ulimit -c unlimited
@@ -265,7 +316,7 @@
CRASHED=$(./script/test check_crash | tail -1)
[[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
echo "CRASHED=$CRASHED" >> $GITHUB_ENV
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() && env.CRASHED == '1' }}
with:
name: core-expect-1-3
@@ -274,7 +325,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-expects
path: tmp/coverage.info
@@ -297,7 +348,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -324,12 +375,12 @@
CRASHED=$(./script/test check_crash | tail -1)
[[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
echo "CRASHED=$CRASHED" >> $GITHUB_ENV
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() }}
with:
name: thread-1-3-posix-pcaps
path: "*.pcap"
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: ${{ failure() && env.CRASHED == '1' }}
with:
name: core-thread-1-3-posix
@@ -338,7 +389,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-thread-1-3-posix
path: tmp/coverage.info
@@ -358,13 +409,13 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
run: |
sudo apt-get --no-install-recommends install -y lcov
- - uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
+ - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with:
path: coverage/
pattern: cov-*
@@ -373,7 +424,7 @@
run: |
script/test combine_coverage
- name: Upload Coverage
- uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
+ uses: codecov/codecov-action@0cfda1dd0a4ad9efc75517f399d859cd1ea4ced1 # v4.0.2
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
diff --git a/.github/workflows/size.yml b/.github/workflows/size.yml
index 7b0358b..98de9d7 100644
--- a/.github/workflows/size.yml
+++ b/.github/workflows/size.yml
@@ -53,7 +53,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- name: Run
env:
OT_BASE_BRANCH: "${{ github.base_ref }}"
diff --git a/.github/workflows/toranj.yml b/.github/workflows/toranj.yml
index 29f2d8b..6b79b13 100644
--- a/.github/workflows/toranj.yml
+++ b/.github/workflows/toranj.yml
@@ -63,7 +63,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -94,7 +94,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -111,7 +111,7 @@
if: "matrix.TORANJ_RADIO != 'multi'"
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: "matrix.TORANJ_RADIO != 'multi'"
with:
name: cov-toranj-cli-${{ matrix.TORANJ_RADIO }}
@@ -127,7 +127,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -159,6 +159,28 @@
git clean -dfx
./tests/toranj/build.sh --enable-plat-key-ref all
+ toranj-macos:
+ name: toranj-macos
+ runs-on: macos-14
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
+ with:
+ egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
+
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
+ with:
+ submodules: true
+ - name: Bootstrap
+ env:
+ GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+ run: |
+ brew update
+ brew install ninja
+ - name: Build & Run
+ run: |
+ ./tests/toranj/build.sh posix-15.4
+
upload-coverage:
needs:
- toranj-cli
@@ -169,13 +191,13 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
run: |
sudo apt-get --no-install-recommends install -y lcov
- - uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
+ - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with:
path: coverage/
pattern: cov-*
@@ -184,7 +206,7 @@
run: |
script/test combine_coverage
- name: Upload Coverage
- uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
+ uses: codecov/codecov-action@0cfda1dd0a4ad9efc75517f399d859cd1ea4ced1 # v4.0.2
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml
index 672aac3..bf685ca 100644
--- a/.github/workflows/unit.yml
+++ b/.github/workflows/unit.yml
@@ -53,7 +53,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Build
@@ -71,7 +71,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
@@ -93,7 +93,7 @@
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- - uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
+ - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: cov-unit-tests
path: tmp/coverage.info
@@ -108,13 +108,13 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Bootstrap
run: |
sudo apt-get --no-install-recommends install -y lcov
- - uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
+ - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with:
path: coverage/
pattern: cov-*
@@ -123,7 +123,7 @@
run: |
script/test combine_coverage
- name: Upload Coverage
- uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
+ uses: codecov/codecov-action@0cfda1dd0a4ad9efc75517f399d859cd1ea4ced1 # v4.0.2
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml
index 5b6d3e7..16dc637 100644
--- a/.github/workflows/version.yml
+++ b/.github/workflows/version.yml
@@ -49,7 +49,7 @@
with:
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
- - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
submodules: true
- name: Check
diff --git a/doc/ot_config_doc.h b/doc/ot_config_doc.h
index 564950b..87af229 100644
--- a/doc/ot_config_doc.h
+++ b/doc/ot_config_doc.h
@@ -47,15 +47,15 @@
* @defgroup config-channel-manager Channel Manager
* @defgroup config-channel-monitor Channel Monitor
* @defgroup config-child-supervision Child Supervision
- * @defgroup config-coap CoAP
- * @defgroup config-commissioner Commissioner
+ * @defgroup config-coap CoAP
+ * @defgroup config-commissioner Commissioner
* @defgroup config-crypto Crypto Backend Library
* @defgroup config-dataset-updater Dataset Updater
* @defgroup config-dhcpv6-client DHCPv6 Client
* @defgroup config-dhcpv6-server DHCPv6 Server
* @defgroup config-diag DIAG Service
- * @defgroup config-dns-client DNS Client
- * @defgroup config-dns-dso DNS Stateful Operations
+ * @defgroup config-dns-client DNS Client
+ * @defgroup config-dns-dso DNS Stateful Operations
* @defgroup config-dnssd-server DNS-SD Server
* @defgroup config-history-tracker History Tracker
* @defgroup config-ip6 IP6 Service
@@ -83,7 +83,7 @@
* @defgroup config-srp-server SRP Server
* @defgroup config-time-sync Time Sync Service
* @defgroup config-tmf Thread Management Framework Service
- * @defgroup config-trel TREL
+ * @defgroup config-trel TREL
*
* @}
*
diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake
index bad9193..da77ef0 100644
--- a/etc/cmake/options.cmake
+++ b/etc/cmake/options.cmake
@@ -180,6 +180,7 @@
ot_option(OT_BORDER_ROUTING_DHCP6_PD OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE "dhcpv6 pd support in border routing")
ot_option(OT_BORDER_ROUTING_COUNTERS OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE "border routing counters")
ot_option(OT_CHANNEL_MANAGER OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE "channel manager")
+ot_option(OT_CHANNEL_MANAGER_CSL OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE "channel manager for csl channel")
ot_option(OT_CHANNEL_MONITOR OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE "channel monitor")
ot_option(OT_COAP OPENTHREAD_CONFIG_COAP_API_ENABLE "coap api")
ot_option(OT_COAP_BLOCK OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE "coap block-wise transfer (RFC7959)")
@@ -278,7 +279,7 @@
endif()
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-set(OT_THREAD_VERSION_VALUES "1.1" "1.2" "1.3" "1.3.1")
+set(OT_THREAD_VERSION_VALUES "1.1" "1.2" "1.3" "1.3.1" "1.4")
set(OT_THREAD_VERSION "1.3" CACHE STRING "set Thread version")
set_property(CACHE OT_THREAD_VERSION PROPERTY STRINGS "${OT_THREAD_VERSION_VALUES}")
list(FIND OT_THREAD_VERSION_VALUES "${OT_THREAD_VERSION}" ot_index)
@@ -286,7 +287,7 @@
message(STATUS "OT_THREAD_VERSION=\"${OT_THREAD_VERSION}\"")
message(FATAL_ERROR "Invalid value for OT_THREAD_VERSION - valid values are: " "${OT_THREAD_VERSION_VALUES}")
endif()
-set(OT_VERSION_SUFFIX_LIST "1_1" "1_2" "1_3" "1_3_1")
+set(OT_VERSION_SUFFIX_LIST "1_1" "1_2" "1_3" "1_3_1" "1_4")
list(GET OT_VERSION_SUFFIX_LIST ${ot_index} OT_VERSION_SUFFIX)
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_${OT_VERSION_SUFFIX}")
message(STATUS "OT_THREAD_VERSION=\"${OT_THREAD_VERSION}\" -> OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_${OT_VERSION_SUFFIX}")
diff --git a/examples/config/ot-core-config-check-size-br.h b/examples/config/ot-core-config-check-size-br.h
index 8310019..e01bc9c 100644
--- a/examples/config/ot-core-config-check-size-br.h
+++ b/examples/config/ot-core-config-check-size-br.h
@@ -40,6 +40,7 @@
#define OPENTHREAD_CONFIG_ASSERT_ENABLE 1
#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 1
#define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 1
+#define OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 1
#define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 1
#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1
#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 1
diff --git a/examples/config/ot-core-config-check-size-ftd.h b/examples/config/ot-core-config-check-size-ftd.h
index 63e7ad4..bdd69d0 100644
--- a/examples/config/ot-core-config-check-size-ftd.h
+++ b/examples/config/ot-core-config-check-size-ftd.h
@@ -41,6 +41,7 @@
#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 0
#define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 1
#define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 1
+#define OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 1
#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 0
#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 0
#define OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE 0
diff --git a/examples/config/ot-core-config-check-size-mtd.h b/examples/config/ot-core-config-check-size-mtd.h
index 88b2c11..d5ca74c 100644
--- a/examples/config/ot-core-config-check-size-mtd.h
+++ b/examples/config/ot-core-config-check-size-mtd.h
@@ -41,6 +41,7 @@
#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 0
#define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 0
#define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 0
+#define OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 0
#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 0
#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 0
#define OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE 0
diff --git a/examples/platforms/simulation/CMakeLists.txt b/examples/platforms/simulation/CMakeLists.txt
index 41a1dbf..12580e2 100644
--- a/examples/platforms/simulation/CMakeLists.txt
+++ b/examples/platforms/simulation/CMakeLists.txt
@@ -72,6 +72,7 @@
misc.c
multipan.c
radio.c
+ simul_utils.c
spi-stubs.c
system.c
trel.c
diff --git a/examples/platforms/simulation/ble.c b/examples/platforms/simulation/ble.c
index 2fe3c64..d48922a 100644
--- a/examples/platforms/simulation/ble.c
+++ b/examples/platforms/simulation/ble.c
@@ -26,50 +26,191 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
+#include "platform-simulation.h"
+
+#include <errno.h>
+
+#include <stdio.h>
+#include <stdlib.h>
#include <openthread/platform/ble.h>
+#include "openthread/error.h"
+#include "utils/code_utils.h"
+
+#define PLAT_BLE_MSG_DATA_MAX 2048
+static uint8_t sBleBuffer[PLAT_BLE_MSG_DATA_MAX];
+
+static int sFd = -1;
+
+static const uint16_t kPortBase = 10000;
+static uint16_t sPort = 0;
+struct sockaddr_in sSockaddr;
+
+static void initFds(void)
+{
+ int fd;
+ int one = 1;
+ struct sockaddr_in sockaddr;
+
+ memset(&sockaddr, 0, sizeof(sockaddr));
+
+ sPort = (uint16_t)(kPortBase + gNodeId);
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(sPort);
+ sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
+
+ otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sFd)"));
+
+ otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != -1,
+ perror("setsockopt(sFd, SO_REUSEADDR)"));
+ otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != -1,
+ perror("setsockopt(sFd, SO_REUSEPORT)"));
+
+ otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sFd)"));
+
+ // Fd is successfully initialized.
+ sFd = fd;
+
+exit:
+ if (sFd == -1)
+ {
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void deinitFds(void)
+{
+ if (sFd != -1)
+ {
+ close(sFd);
+ sFd = -1;
+ }
+}
+
otError otPlatBleEnable(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
- return OT_ERROR_NOT_IMPLEMENTED;
+ initFds();
+ return OT_ERROR_NONE;
}
otError otPlatBleDisable(otInstance *aInstance)
{
+ deinitFds();
OT_UNUSED_VARIABLE(aInstance);
- return OT_ERROR_NOT_IMPLEMENTED;
+ return OT_ERROR_NONE;
}
otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aInterval);
- return OT_ERROR_NOT_IMPLEMENTED;
+ return OT_ERROR_NONE;
}
otError otPlatBleGapAdvStop(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
- return OT_ERROR_NOT_IMPLEMENTED;
+ return OT_ERROR_NONE;
}
otError otPlatBleGapDisconnect(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
- return OT_ERROR_NOT_IMPLEMENTED;
+ return OT_ERROR_NONE;
}
otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu)
{
OT_UNUSED_VARIABLE(aInstance);
- OT_UNUSED_VARIABLE(aMtu);
- return OT_ERROR_NOT_IMPLEMENTED;
+ *aMtu = PLAT_BLE_MSG_DATA_MAX - 1;
+ return OT_ERROR_NONE;
}
otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aHandle);
+
+ ssize_t rval;
+ otError error = OT_ERROR_NONE;
+
+ otEXPECT_ACTION(sFd != -1, error = OT_ERROR_INVALID_STATE);
+ rval = sendto(sFd, (const char *)aPacket->mValue, aPacket->mLength, 0, (struct sockaddr *)&sSockaddr,
+ sizeof(sSockaddr));
+ if (rval == -1)
+ {
+ perror("BLE simulation sendto failed.");
+ }
+
+exit:
+ return error;
+}
+
+void platformBleDeinit(void) { deinitFds(); }
+
+void platformBleUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd)
+{
+ OT_UNUSED_VARIABLE(aTimeout);
+ OT_UNUSED_VARIABLE(aWriteFdSet);
+
+ if (aReadFdSet != NULL && sFd != -1)
+ {
+ FD_SET(sFd, aReadFdSet);
+
+ if (aMaxFd != NULL && *aMaxFd < sFd)
+ {
+ *aMaxFd = sFd;
+ }
+ }
+}
+
+void platformBleProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
+{
+ OT_UNUSED_VARIABLE(aWriteFdSet);
+
+ otEXPECT(sFd != -1);
+
+ if (FD_ISSET(sFd, aReadFdSet))
+ {
+ socklen_t len = sizeof(sSockaddr);
+ ssize_t rval;
+ memset(&sSockaddr, 0, sizeof(sSockaddr));
+ rval = recvfrom(sFd, sBleBuffer, sizeof(sBleBuffer), 0, (struct sockaddr *)&sSockaddr, &len);
+ if (rval > 0)
+ {
+ otBleRadioPacket myPacket;
+ myPacket.mValue = sBleBuffer;
+ myPacket.mLength = (uint16_t)rval;
+ myPacket.mPower = 0;
+ otPlatBleGattServerOnWriteRequest(
+ aInstance, 0,
+ &myPacket); // TODO consider passing otPlatBleGattServerOnWriteRequest as a callback function
+ }
+ else if (rval == 0)
+ {
+ // socket is closed, which should not happen
+ assert(false);
+ }
+ else if (errno != EINTR && errno != EAGAIN)
+ {
+ perror("recvfrom BLE simulation failed");
+ exit(EXIT_FAILURE);
+ }
+ }
+exit:
+ return;
+}
+
+OT_TOOL_WEAK void otPlatBleGattServerOnWriteRequest(otInstance *aInstance,
+ uint16_t aHandle,
+ const otBleRadioPacket *aPacket)
+{
+ OT_UNUSED_VARIABLE(aInstance);
+ OT_UNUSED_VARIABLE(aHandle);
OT_UNUSED_VARIABLE(aPacket);
- return OT_ERROR_NOT_IMPLEMENTED;
+ assert(false);
+ /* In case of rcp there is a problem with linking to otPlatBleGattServerOnWriteRequest
+ * which is available in FTD/MTD library.
+ */
}
diff --git a/examples/platforms/simulation/infra_if.c b/examples/platforms/simulation/infra_if.c
index 9596406..ccb5e9b 100644
--- a/examples/platforms/simulation/infra_if.c
+++ b/examples/platforms/simulation/infra_if.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021, The OpenThread Authors.
+ * Copyright (c) 2024, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,15 +28,154 @@
#include "platform-simulation.h"
+#include <openthread/icmp6.h>
+#include <openthread/ip6.h>
+#include <openthread/logging.h>
#include <openthread/platform/infra_if.h>
-#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+#include "simul_utils.h"
+#include "utils/code_utils.h"
+
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && !OPENTHREAD_RADIO
+
+#define DEBUG_LOG 0
+
+#if DEBUG_LOG
+#define LOG(...) otLogNotePlat("[infra-if] "__VA_ARGS__)
+#else
+#define LOG(...) \
+ do \
+ { \
+ } while (0)
+#endif
+
+#define INFRA_IF_SIM_PORT 9800
+#define INFRA_IF_MAX_PACKET_SIZE 1800
+#define INFRA_IF_MAX_PENDING_TX 64
+#define INFRA_IF_NEIGHBOR_ADVERT_SIZE 24
+
+typedef struct Message
+{
+ uint32_t mIfIndex;
+ otIp6Address mSrc;
+ otIp6Address mDst;
+ uint16_t mDataLength;
+ uint8_t mData[INFRA_IF_MAX_PACKET_SIZE];
+} Message;
+
+static bool sInitialized = false;
+static otIp6Address sIp6Address;
+static otIp6Address sLinkLocalAllNodes;
+static otIp6Address sLinkLocalAllRouters;
+static utilsSocket sSocket;
+static uint16_t sPortOffset = 0;
+static uint8_t sNumPendingTx = 0;
+static Message sPendingTx[INFRA_IF_MAX_PENDING_TX];
+
+//---------------------------------------------------------------------------------------------------------------------
+
+static bool addressesMatch(const otIp6Address *aFirstAddr, const otIp6Address *aSecondAddr)
+{
+ return memcmp(aFirstAddr, aSecondAddr, sizeof(otIp6Address)) == 0;
+}
+
+static uint16_t getMessageSize(const Message *aMessage)
+{
+ return (uint16_t)(&aMessage->mData[aMessage->mDataLength] - (const uint8_t *)aMessage);
+}
+
+static void sendPendingTxMessages(void)
+{
+ for (uint8_t i = 0; i < sNumPendingTx; i++)
+ {
+ utilsSendOverSocket(&sSocket, &sPendingTx[i], getMessageSize(&sPendingTx[i]));
+ }
+
+ sNumPendingTx = 0;
+}
+
+static void sendNeighborAdvert(const Message *aNsMessage)
+{
+ Message *message;
+ uint8_t index;
+
+ assert(sNumPendingTx < INFRA_IF_MAX_PENDING_TX);
+
+ message = &sPendingTx[sNumPendingTx++];
+
+ message->mIfIndex = aNsMessage->mIfIndex;
+ message->mSrc = sIp6Address;
+ message->mDst = aNsMessage->mSrc;
+
+ // Neighbor Advertisement Message (RFC 4861)
+ //
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | Type | Code | Checksum |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // |R|S|O| Reserved |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | |
+ // + +
+ // | |
+ // + Target Address +
+ // | |
+ // + +
+ // | |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ index = 0;
+ memset(message->mData, 0, INFRA_IF_NEIGHBOR_ADVERT_SIZE);
+
+ message->mData[index++] = OT_ICMP6_TYPE_NEIGHBOR_ADVERT; // Type.
+ index += 3; // Code is zero. Checksum (uint16) as zero.
+ message->mData[index++] = 0xd0; // Flags, set R and S bits.
+ index += 3; // Skip over the reserved bytes.
+ memcpy(&message->mData[index], &sIp6Address, sizeof(sIp6Address)); // Set the target address field.
+ index += sizeof(sIp6Address);
+
+ assert(index == INFRA_IF_NEIGHBOR_ADVERT_SIZE);
+
+ message->mDataLength = INFRA_IF_NEIGHBOR_ADVERT_SIZE;
+}
+
+static void processMessage(otInstance *aInstance, Message *aMessage, uint16_t aLength)
+{
+ OT_UNUSED_VARIABLE(aInstance);
+
+ otEXPECT(aLength > 0);
+ otEXPECT(getMessageSize(aMessage) == aLength);
+ otEXPECT(aMessage->mDataLength > 0);
+
+ // Validate the dest address.
+ otEXPECT(addressesMatch(&aMessage->mDst, &sIp6Address) || addressesMatch(&aMessage->mDst, &sLinkLocalAllNodes) ||
+ addressesMatch(&aMessage->mDst, &sLinkLocalAllRouters));
+
+ if (aMessage->mData[0] == OT_ICMP6_TYPE_NEIGHBOR_SOLICIT)
+ {
+ LOG("Received NS, responding with NA");
+ sendNeighborAdvert(aMessage);
+ }
+ else
+ {
+ LOG("Received msg, len:%u", aMessage->mDataLength);
+ otPlatInfraIfRecvIcmp6Nd(aInstance, aMessage->mIfIndex, &aMessage->mSrc, aMessage->mData,
+ aMessage->mDataLength);
+ }
+
+exit:
+ return;
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+// otPlatInfraIf
+
bool otPlatInfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddress)
{
OT_UNUSED_VARIABLE(aInfraIfIndex);
- OT_UNUSED_VARIABLE(aAddress);
- return false;
+ return addressesMatch(aAddress, &sIp6Address);
}
otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex,
@@ -44,12 +183,27 @@
const uint8_t *aBuffer,
uint16_t aBufferLength)
{
- OT_UNUSED_VARIABLE(aInfraIfIndex);
- OT_UNUSED_VARIABLE(aDestAddress);
- OT_UNUSED_VARIABLE(aBuffer);
- OT_UNUSED_VARIABLE(aBufferLength);
+ otError error = OT_ERROR_FAILED;
+ Message *message;
- return OT_ERROR_NONE;
+ otEXPECT(sInitialized);
+ otEXPECT(sNumPendingTx < INFRA_IF_MAX_PENDING_TX);
+
+ message = &sPendingTx[sNumPendingTx++];
+
+ message->mIfIndex = aInfraIfIndex;
+ message->mSrc = sIp6Address;
+ message->mDst = *aDestAddress;
+
+ assert(aBufferLength <= INFRA_IF_MAX_PACKET_SIZE);
+ message->mDataLength = aBufferLength;
+ memcpy(message->mData, aBuffer, aBufferLength);
+ error = OT_ERROR_NONE;
+
+ LOG("otPlatInfraIfSendIcmp6Nd() msg-len:%u", aBufferLength);
+
+exit:
+ return error;
}
otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex)
@@ -58,4 +212,127 @@
return OT_ERROR_NONE;
}
-#endif
+
+//---------------------------------------------------------------------------------------------------------------------
+// platformInfraIf
+
+void platformInfraIfInit(void)
+{
+ char *str;
+
+ otEXPECT(!sInitialized);
+
+ sInitialized = true;
+
+ memset(&sIp6Address, 0, sizeof(sIp6Address));
+ sIp6Address.mFields.m8[0] = 0xfe;
+ sIp6Address.mFields.m8[1] = 0x80;
+ sIp6Address.mFields.m8[15] = (uint8_t)(gNodeId & 0xff);
+
+ // "ff02::01"
+ memset(&sLinkLocalAllNodes, 0, sizeof(sLinkLocalAllNodes));
+ sLinkLocalAllNodes.mFields.m8[0] = 0xff;
+ sLinkLocalAllNodes.mFields.m8[1] = 0x02;
+ sLinkLocalAllNodes.mFields.m8[15] = 0x01;
+
+ // "ff02::02"
+ memset(&sLinkLocalAllRouters, 0, sizeof(sLinkLocalAllRouters));
+ sLinkLocalAllRouters.mFields.m8[0] = 0xff;
+ sLinkLocalAllRouters.mFields.m8[1] = 0x02;
+ sLinkLocalAllRouters.mFields.m8[15] = 0x02;
+
+ str = getenv("PORT_OFFSET");
+
+ if (str != NULL)
+ {
+ char *endptr;
+
+ sPortOffset = (uint16_t)strtol(str, &endptr, 0);
+
+ if (*endptr != '\0')
+ {
+ fprintf(stderr, "\r\nInvalid PORT_OFFSET: %s\r\n", str);
+ exit(EXIT_FAILURE);
+ }
+
+ sPortOffset *= (MAX_NETWORK_SIZE + 1);
+ }
+
+ utilsInitSocket(&sSocket, INFRA_IF_SIM_PORT + sPortOffset);
+
+exit:
+ return;
+}
+
+void platformInfraIfDeinit(void)
+{
+ otEXPECT(sInitialized);
+ sInitialized = false;
+ utilsDeinitSocket(&sSocket);
+
+exit:
+ return;
+}
+
+void platformInfraIfUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd)
+{
+ otEXPECT(sInitialized);
+
+ utilsAddSocketRxFd(&sSocket, aReadFdSet, aMaxFd);
+
+ if (sNumPendingTx > 0)
+ {
+ utilsAddSocketTxFd(&sSocket, aWriteFdSet, aMaxFd);
+ }
+
+exit:
+ return;
+}
+
+void platformInfraIfProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
+{
+ OT_UNUSED_VARIABLE(aInstance);
+
+ otEXPECT(sInitialized);
+
+ if ((sNumPendingTx > 0) && utilsCanSocketSend(&sSocket, aWriteFdSet))
+ {
+ sendPendingTxMessages();
+ }
+
+ if (utilsCanSocketReceive(&sSocket, aReadFdSet))
+ {
+ Message message;
+ uint16_t len;
+
+ message.mDataLength = 0;
+
+ len = utilsReceiveFromSocket(&sSocket, &message, sizeof(message), NULL);
+ processMessage(aInstance, &message, len);
+ }
+
+exit:
+ return;
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+// Provide weak implementation (used for RCP builds).
+// `OPENTHREAD_RADIO` is not available in simulation platform
+
+OT_TOOL_WEAK void otPlatInfraIfRecvIcmp6Nd(otInstance *aInstance,
+ uint32_t aInfraIfIndex,
+ const otIp6Address *aSrcAddress,
+ const uint8_t *aBuffer,
+ uint16_t aBufferLength)
+{
+ OT_UNUSED_VARIABLE(aInstance);
+ OT_UNUSED_VARIABLE(aInfraIfIndex);
+ OT_UNUSED_VARIABLE(aSrcAddress);
+ OT_UNUSED_VARIABLE(aBuffer);
+ OT_UNUSED_VARIABLE(aBufferLength);
+
+ fprintf(stderr, "\n\r Weak otPlatInfraIfRecvIcmp6Nd is being used\n\r");
+ exit(1);
+}
+
+#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
diff --git a/examples/platforms/simulation/platform-simulation.h b/examples/platforms/simulation/platform-simulation.h
index 0592b14..d440768 100644
--- a/examples/platforms/simulation/platform-simulation.h
+++ b/examples/platforms/simulation/platform-simulation.h
@@ -159,7 +159,7 @@
void platformRadioReceive(otInstance *aInstance, uint8_t *aBuf, uint16_t aBufLength);
/**
- * Updates the file descriptor sets with file descriptors used by the radio driver.
+ * Updates the file descriptor sets with file descriptors used by the BLE radio driver.
*
* @param[in,out] aReadFdSet A pointer to the read file descriptors.
* @param[in,out] aWriteFdSet A pointer to the write file descriptors.
@@ -305,4 +305,67 @@
#endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+
+/**
+ * Initializes the platform infra-if module.
+ *
+ */
+void platformInfraIfInit(void);
+
+/**
+ * Shuts down the platform infra-if module.
+ *
+ */
+void platformInfraIfDeinit(void);
+
+/**
+ * Updates the file descriptor sets with file descriptors used by the infra-if module
+ *
+ * @param[in,out] aReadFdSet A pointer to the read file descriptors.
+ * @param[in,out] aWriteFdSet A pointer to the write file descriptors.
+ * @param[in,out] aMaxFd A pointer to the max file descriptor.
+ *
+ */
+void platformInfraIfUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd);
+
+/**
+ * Performs infra-if module processing.
+ *
+ * @param[in] aInstance The OpenThread instance structure.
+ * @param[in] aReadFdSet A pointer to the read file descriptors.
+ * @param[in] aWriteFdSet A pointer to the write file descriptors.
+ *
+ */
+void platformInfraIfProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet);
+
+#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+
+/**
+ * Shuts down the BLE service used by OpenThread.
+ *
+ */
+void platformBleDeinit(void);
+
+/**
+ * Updates the file descriptor sets with file descriptors used by the radio driver.
+ *
+ * @param[in,out] aReadFdSet A pointer to the read file descriptors.
+ * @param[in,out] aWriteFdSet A pointer to the write file descriptors.
+ * @param[in,out] aTimeout A pointer to the timeout.
+ * @param[in,out] aMaxFd A pointer to the max file descriptor.
+ *
+ */
+void platformBleUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd);
+
+/**
+ * Performs BLE driver processing.
+ *
+ * @param[in] aInstance The OpenThread instance structure.
+ * @param[in] aReadFdSet A pointer to the read file descriptors.
+ * @param[in] aWriteFdSet A pointer to the write file descriptors.
+ *
+ */
+void platformBleProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet);
+
#endif // PLATFORM_SIMULATION_H_
diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c
index 03808b5..960d8c6 100644
--- a/examples/platforms/simulation/radio.c
+++ b/examples/platforms/simulation/radio.c
@@ -41,14 +41,12 @@
#include <openthread/platform/radio.h>
#include <openthread/platform/time.h>
+#include "simul_utils.h"
#include "utils/code_utils.h"
#include "utils/link_metrics.h"
#include "utils/mac_frame.h"
#include "utils/soft_source_match_table.h"
-// The IPv4 group for receiving packets of radio simulation
-#define OT_RADIO_GROUP "224.0.0.116"
-
#define MS_PER_S 1000
#define US_PER_MS 1000
@@ -75,11 +73,9 @@
extern uint16_t sPortBase;
extern uint16_t sPortOffset;
#else
-static int sTxFd = -1;
-static int sRxFd = -1;
-static uint16_t sPortBase = 9000;
-static uint16_t sPortOffset = 0;
-static uint16_t sPort = 0;
+static utilsSocket sSocket;
+static uint16_t sPortBase = 9000;
+static uint16_t sPortOffset = 0;
#endif
static int8_t sEnergyScanResult = OT_RADIO_RSSI_INVALID;
@@ -190,6 +186,8 @@
{
bool isConnectable = true;
+ otEXPECT_ACTION(aNodeId != gNodeId, isConnectable = false);
+
switch (sFilterMode)
{
case kFilterOff:
@@ -202,6 +200,7 @@
break;
}
+exit:
return isConnectable;
}
@@ -407,82 +406,15 @@
sPromiscuous = aEnable;
}
-#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
-static void initFds(void)
-{
- int fd;
- int one = 1;
- struct sockaddr_in sockaddr;
-
- memset(&sockaddr, 0, sizeof(sockaddr));
-
- otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sTxFd)"));
-
- sPort = (uint16_t)(sPortBase + sPortOffset + gNodeId);
- sockaddr.sin_family = AF_INET;
- sockaddr.sin_port = htons(sPort);
- sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
-
- otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &sockaddr.sin_addr, sizeof(sockaddr.sin_addr)) != -1,
- perror("setsockopt(sTxFd, IP_MULTICAST_IF)"));
-
- otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)) != -1,
- perror("setsockopt(sRxFd, IP_MULTICAST_LOOP)"));
-
- otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sTxFd)"));
-
- // Tx fd is successfully initialized.
- sTxFd = fd;
-
- otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sRxFd)"));
-
- otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != -1,
- perror("setsockopt(sRxFd, SO_REUSEADDR)"));
- otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != -1,
- perror("setsockopt(sRxFd, SO_REUSEPORT)"));
-
- {
- struct ip_mreqn mreq;
-
- memset(&mreq, 0, sizeof(mreq));
- inet_pton(AF_INET, OT_RADIO_GROUP, &mreq.imr_multiaddr);
-
- // Always use loopback device to send simulation packets.
- mreq.imr_address.s_addr = inet_addr("127.0.0.1");
-
- otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq.imr_address, sizeof(mreq.imr_address)) != -1,
- perror("setsockopt(sRxFd, IP_MULTICAST_IF)"));
- otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != -1,
- perror("setsockopt(sRxFd, IP_ADD_MEMBERSHIP)"));
- }
-
- sockaddr.sin_family = AF_INET;
- sockaddr.sin_port = htons((uint16_t)(sPortBase + sPortOffset));
- sockaddr.sin_addr.s_addr = inet_addr(OT_RADIO_GROUP);
-
- otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sRxFd)"));
-
- // Rx fd is successfully initialized.
- sRxFd = fd;
-
-exit:
- if (sRxFd == -1 || sTxFd == -1)
- {
- exit(EXIT_FAILURE);
- }
-}
-#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
-
void platformRadioInit(void)
{
-#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
+#if !OPENTHREAD_SIMULATION_VIRTUAL_TIME
parseFromEnvAsUint16("PORT_BASE", &sPortBase);
-
parseFromEnvAsUint16("PORT_OFFSET", &sPortOffset);
sPortOffset *= (MAX_NETWORK_SIZE + 1);
- initFds();
-#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
+ utilsInitSocket(&sSocket, sPortBase + sPortOffset);
+#endif
sReceiveFrame.mPsdu = sReceiveMessage.mPsdu;
sTransmitFrame.mPsdu = sTransmitMessage.mPsdu;
@@ -859,24 +791,14 @@
#else
void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd)
{
- if (aReadFdSet != NULL && (sState != OT_RADIO_STATE_TRANSMIT || sTxWait))
+ if (sState != OT_RADIO_STATE_TRANSMIT || sTxWait)
{
- FD_SET(sRxFd, aReadFdSet);
-
- if (aMaxFd != NULL && *aMaxFd < sRxFd)
- {
- *aMaxFd = sRxFd;
- }
+ utilsAddSocketRxFd(&sSocket, aReadFdSet, aMaxFd);
}
- if (aWriteFdSet != NULL && platformRadioIsTransmitPending())
+ if (platformRadioIsTransmitPending())
{
- FD_SET(sTxFd, aWriteFdSet);
-
- if (aMaxFd != NULL && *aMaxFd < sTxFd)
- {
- *aMaxFd = sTxFd;
- }
+ utilsAddSocketTxFd(&sSocket, aWriteFdSet, aMaxFd);
}
if (sEnergyScanning)
@@ -900,18 +822,7 @@
}
// no need to close in virtual time mode.
-void platformRadioDeinit(void)
-{
- if (sRxFd != -1)
- {
- close(sRxFd);
- }
-
- if (sTxFd != -1)
- {
- close(sTxFd);
- }
-}
+void platformRadioDeinit(void) { utilsDeinitSocket(&sSocket); }
#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME
void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
@@ -919,41 +830,22 @@
OT_UNUSED_VARIABLE(aReadFdSet);
OT_UNUSED_VARIABLE(aWriteFdSet);
-#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
- if (FD_ISSET(sRxFd, aReadFdSet))
+#if !OPENTHREAD_SIMULATION_VIRTUAL_TIME
+ if (utilsCanSocketReceive(&sSocket, aReadFdSet))
{
- struct sockaddr_in sockaddr;
- socklen_t len = sizeof(sockaddr);
- ssize_t rval;
+ uint16_t senderNodeId;
+ uint16_t len;
- memset(&sockaddr, 0, sizeof(sockaddr));
- rval =
- recvfrom(sRxFd, (char *)&sReceiveMessage, sizeof(sReceiveMessage), 0, (struct sockaddr *)&sockaddr, &len);
+ len = utilsReceiveFromSocket(&sSocket, &sReceiveMessage, sizeof(sReceiveMessage), &senderNodeId);
- if (rval > 0)
+ if (NodeIdFilterIsConnectable(senderNodeId))
{
- uint16_t srcPort = ntohs(sockaddr.sin_port);
- uint16_t srcNodeId = srcPort - sPortOffset - sPortBase;
-
- if (NodeIdFilterIsConnectable(srcNodeId) && srcPort != sPort)
- {
- sReceiveFrame.mLength = (uint16_t)(rval - 1);
-
- radioReceive(aInstance);
- }
- }
- else if (rval == 0)
- {
- // socket is closed, which should not happen
- assert(false);
- }
- else if (errno != EINTR && errno != EAGAIN)
- {
- perror("recvfrom(sRxFd)");
- exit(EXIT_FAILURE);
+ sReceiveFrame.mLength = len - 1;
+ radioReceive(aInstance);
}
}
-#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
+#endif
+
if (platformRadioIsTransmitPending())
{
radioSendMessage(aInstance);
@@ -968,33 +860,17 @@
void radioTransmit(struct RadioMessage *aMessage, const struct otRadioFrame *aFrame)
{
-#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
- ssize_t rval;
- struct sockaddr_in sockaddr;
-
- memset(&sockaddr, 0, sizeof(sockaddr));
- sockaddr.sin_family = AF_INET;
- inet_pton(AF_INET, OT_RADIO_GROUP, &sockaddr.sin_addr);
-
- sockaddr.sin_port = htons((uint16_t)(sPortBase + sPortOffset));
- rval =
- sendto(sTxFd, (const char *)aMessage, 1 + aFrame->mLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
-
- if (rval < 0)
- {
- perror("sendto(sTxFd)");
- exit(EXIT_FAILURE);
- }
-#else // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
+#if !OPENTHREAD_SIMULATION_VIRTUAL_TIME
+ utilsSendOverSocket(&sSocket, aMessage, aFrame->mLength + 1); // + 1 is for `mChannel`
+#else
struct Event event;
event.mDelay = 1; // 1us for now
event.mEvent = OT_SIM_EVENT_RADIO_RECEIVED;
event.mDataLength = 1 + aFrame->mLength; // include channel in first byte
memcpy(event.mData, aMessage, event.mDataLength);
-
otSimSendEvent(&event);
-#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
+#endif
}
void radioSendAck(void)
diff --git a/examples/platforms/simulation/simul_utils.c b/examples/platforms/simulation/simul_utils.c
new file mode 100644
index 0000000..36b69af
--- /dev/null
+++ b/examples/platforms/simulation/simul_utils.c
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ */
+
+#include "simul_utils.h"
+
+#include <errno.h>
+#include <sys/time.h>
+
+#include "utils/code_utils.h"
+
+#define UTILS_SOCKET_LOCAL_HOST_ADDR "127.0.0.1"
+#define UTILS_SOCKET_GROUP_ADDR "224.0.0.116"
+
+const char *gLocalHost = UTILS_SOCKET_LOCAL_HOST_ADDR;
+
+void utilsAddFdToFdSet(int aFd, fd_set *aFdSet, int *aMaxFd)
+{
+ otEXPECT(aFd >= 0);
+ otEXPECT(aFdSet != NULL);
+
+ FD_SET(aFd, aFdSet);
+
+ otEXPECT(aMaxFd != NULL);
+
+ if (*aMaxFd < aFd)
+ {
+ *aMaxFd = aFd;
+ }
+
+exit:
+ return;
+}
+
+void utilsInitSocket(utilsSocket *aSocket, uint16_t aPortBase)
+{
+ int fd;
+ int one = 1;
+ int rval;
+ struct sockaddr_in sockaddr;
+ struct ip_mreqn mreq;
+
+ aSocket->mInitialized = false;
+ aSocket->mPortBase = aPortBase;
+ aSocket->mPort = (uint16_t)(aSocket->mPortBase + gNodeId);
+
+ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // Prepare `mTxFd`
+
+ fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ otEXPECT_ACTION(fd != -1, perror("socket(TxFd)"));
+
+ memset(&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(aSocket->mPort);
+ sockaddr.sin_addr.s_addr = inet_addr(gLocalHost);
+
+ rval = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &sockaddr.sin_addr, sizeof(sockaddr.sin_addr));
+ otEXPECT_ACTION(rval != -1, perror("setsockopt(TxFd, IP_MULTICAST_IF)"));
+
+ rval = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
+ otEXPECT_ACTION(rval != -1, perror("setsockopt(TxFd, IP_MULTICAST_LOOP)"));
+
+ rval = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
+ otEXPECT_ACTION(rval != -1, perror("bind(TxFd)"));
+
+ aSocket->mTxFd = fd;
+
+ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // Prepare `mRxFd`
+
+ fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ otEXPECT_ACTION(fd != -1, perror("socket(RxFd)"));
+
+ rval = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+ otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, SO_REUSEADDR)"));
+
+ rval = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
+ otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, SO_REUSEPORT)"));
+
+ memset(&mreq, 0, sizeof(mreq));
+ inet_pton(AF_INET, UTILS_SOCKET_GROUP_ADDR, &mreq.imr_multiaddr);
+
+ mreq.imr_address.s_addr = inet_addr(gLocalHost);
+
+ rval = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq.imr_address, sizeof(mreq.imr_address));
+ otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, IP_MULTICAST_IF)"));
+
+ rval = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+ otEXPECT_ACTION(rval != -1, perror("setsockopt(RxFd, IP_ADD_MEMBERSHIP)"));
+
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(aSocket->mPortBase);
+ sockaddr.sin_addr.s_addr = inet_addr(UTILS_SOCKET_GROUP_ADDR);
+
+ rval = bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
+ otEXPECT_ACTION(rval != -1, perror("bind(RxFd)"));
+
+ aSocket->mRxFd = fd;
+
+ aSocket->mInitialized = true;
+
+exit:
+ if (!aSocket->mInitialized)
+ {
+ exit(EXIT_FAILURE);
+ }
+}
+
+void utilsDeinitSocket(utilsSocket *aSocket)
+{
+ if (aSocket->mInitialized)
+ {
+ close(aSocket->mRxFd);
+ close(aSocket->mTxFd);
+ aSocket->mInitialized = false;
+ }
+}
+
+void utilsAddSocketRxFd(const utilsSocket *aSocket, fd_set *aFdSet, int *aMaxFd)
+{
+ otEXPECT(aSocket->mInitialized);
+ utilsAddFdToFdSet(aSocket->mRxFd, aFdSet, aMaxFd);
+
+exit:
+ return;
+}
+
+void utilsAddSocketTxFd(const utilsSocket *aSocket, fd_set *aFdSet, int *aMaxFd)
+{
+ otEXPECT(aSocket->mInitialized);
+ utilsAddFdToFdSet(aSocket->mTxFd, aFdSet, aMaxFd);
+
+exit:
+ return;
+}
+
+bool utilsCanSocketReceive(const utilsSocket *aSocket, const fd_set *aReadFdSet)
+{
+ return aSocket->mInitialized && FD_ISSET(aSocket->mRxFd, aReadFdSet);
+}
+
+bool utilsCanSocketSend(const utilsSocket *aSocket, const fd_set *aWriteFdSet)
+{
+ return aSocket->mInitialized && FD_ISSET(aSocket->mTxFd, aWriteFdSet);
+}
+
+uint16_t utilsReceiveFromSocket(const utilsSocket *aSocket,
+ void *aBuffer,
+ uint16_t aBufferSize,
+ uint16_t *aSenderNodeId)
+{
+ struct sockaddr_in sockaddr;
+ socklen_t socklen = sizeof(sockaddr);
+ ssize_t rval;
+ uint16_t len = 0;
+
+ memset(&sockaddr, 0, sizeof(sockaddr));
+
+ rval = recvfrom(aSocket->mRxFd, (char *)aBuffer, aBufferSize, 0, (struct sockaddr *)&sockaddr, &socklen);
+
+ if (rval > 0)
+ {
+ uint16_t senderPort = ntohs(sockaddr.sin_port);
+
+ if (aSenderNodeId != NULL)
+ {
+ *aSenderNodeId = (uint16_t)(senderPort - aSocket->mPortBase);
+ }
+
+ len = (uint16_t)rval;
+ }
+ else if (rval == 0)
+ {
+ assert(false);
+ }
+ else if (errno != EINTR && errno != EAGAIN)
+ {
+ perror("recvfrom(RxFd)");
+ exit(EXIT_FAILURE);
+ }
+
+ return len;
+}
+
+void utilsSendOverSocket(const utilsSocket *aSocket, const void *aBuffer, uint16_t aBufferLength)
+{
+ ssize_t rval;
+ struct sockaddr_in sockaddr;
+
+ memset(&sockaddr, 0, sizeof(sockaddr));
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_port = htons(aSocket->mPortBase);
+ inet_pton(AF_INET, UTILS_SOCKET_GROUP_ADDR, &sockaddr.sin_addr);
+
+ rval =
+ sendto(aSocket->mTxFd, (const char *)aBuffer, aBufferLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
+
+ if (rval < 0)
+ {
+ perror("sendto(sTxFd)");
+ exit(EXIT_FAILURE);
+ }
+}
diff --git a/examples/platforms/simulation/simul_utils.h b/examples/platforms/simulation/simul_utils.h
new file mode 100644
index 0000000..0d70f2d
--- /dev/null
+++ b/examples/platforms/simulation/simul_utils.h
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+#ifndef PLATFORM_SIMULATION_SOCKET_UTILS_H_
+#define PLATFORM_SIMULATION_SOCKET_UTILS_H_
+
+#include "platform-simulation.h"
+
+/**
+ * Represents a socket for communication with other simulation node.
+ *
+ * This is used for emulation of 15.4 radio or other interfaces.
+ *
+ */
+typedef struct utilsSocket
+{
+ bool mInitialized; ///< Whether or not initialized.
+ int mTxFd; ///< RX file descriptor.
+ int mRxFd; ///< TX file descriptor.
+ uint16_t mPortBase; ///< Base port number value.
+ uint16_t mPort; ///< The port number used by this node
+} utilsSocket;
+
+extern const char *gLocalHost; ///< Local host address to use for sockets
+
+/**
+ * Adds a file descriptor (FD) to a given FD set.
+ *
+ * @param[in] aFd The FD to add.
+ * @param[in] aFdSet The FD set to add to.
+ * @param[in] aMaxFd A pointer to track maximum FD in @p aFdSet (can be NULL).
+ *
+ */
+void utilsAddFdToFdSet(int aFd, fd_set *aFdSet, int *aMaxFd);
+
+/**
+ * Initializes the socket.
+ *
+ * @param[in] aSocket The socket to initialize.
+ * @param[in] aPortBase The base port number value. Nodes will determine their port as `aPortBased + gNodeId`.
+ *
+ */
+void utilsInitSocket(utilsSocket *aSocket, uint16_t aPortBase);
+
+/**
+ * De-initializes the socket.
+ *
+ * @param[in] aSocket The socket to de-initialize.
+ *
+ */
+void utilsDeinitSocket(utilsSocket *aSocket);
+
+/**
+ * Adds sockets RX FD to a given FD set.
+ *
+ * @param[in] aSocket The socket.
+ * @param[in] aFdSet The (read) FD set to add to.
+ * @param[in] aMaxFd A pointer to track maximum FD in @p aFdSet (can be NULL).
+ *
+ */
+void utilsAddSocketRxFd(const utilsSocket *aSocket, fd_set *aFdSet, int *aMaxFd);
+
+/**
+ * Adds sockets TX FD to a given FD set.
+ *
+ * @param[in] aSocket The socket.
+ * @param[in] aFdSet The (write) FD set to add to.
+ * @param[in] aMaxFd A pointer to track maximum FD in @p aFdSet (can be NULL).
+ *
+ */
+void utilsAddSocketTxFd(const utilsSocket *aSocket, fd_set *aFdSet, int *aMaxFd);
+
+/**
+ * Indicates whether the socket can receive.
+ *
+ * @param[in] aSocket The socket.
+ * @param[in] aReadFdSet The read FD set.
+ *
+ * @retval TRUE The socket RX FD is in @p aReadFdSet, and socket can receive.
+ * @retval FALSE The socket RX FD is not in @p aReadFdSet. Socket is not ready to receive.
+ *
+ */
+bool utilsCanSocketReceive(const utilsSocket *aSocket, const fd_set *aReadFdSet);
+
+/**
+ * Indicates whether the socket can send.
+ *
+ * @param[in] aSocket The socket.
+ * @param[in] aFdSet The write FD set.
+ *
+ * @retval TRUE The socket TX FD is in @p aWriteFdSet, and socket can send.
+ * @retval FALSE The socket TX FD is not in @p aWriteFdSet. Socket is not ready to send.
+ *
+ */
+bool utilsCanSocketSend(const utilsSocket *aSocket, const fd_set *aWriteFdSet);
+
+/**
+ * Receives data from socket.
+ *
+ * MUST be used when `utilsCanSocketReceive()` returns `TRUE.
+ *
+ * @param[in] aSocket The socket.
+ * @param[out] aBuffer The buffer to output the read content.
+ * @param[in] aBufferSize Maximum size of buffer in bytes.
+ * @param[out] aSenderNodeId A pointer to return the Node ID of the sender (derived from the port number).
+ * Can be NULL if not needed.
+ *
+ * @returns The number of received bytes written into @p aBuffer.
+ *
+ */
+uint16_t utilsReceiveFromSocket(const utilsSocket *aSocket,
+ void *aBuffer,
+ uint16_t aBufferSize,
+ uint16_t *aSenderNodeId);
+
+/**
+ * Sends data over the socket.
+ *
+ * @param[in] aSocket The socket.
+ * @param[in] aBuffer The buffer containing the bytes to sent.
+ * @param[in] aBufferSize Size of data in @p buffer in bytes.
+ *
+ */
+void utilsSendOverSocket(const utilsSocket *aSocket, const void *aBuffer, uint16_t aBufferLength);
+
+#endif // PLATFORM_SIMULATION_SOCKET_UTILS_H_
diff --git a/examples/platforms/simulation/system.c b/examples/platforms/simulation/system.c
index 535cb1c..2abe892 100644
--- a/examples/platforms/simulation/system.c
+++ b/examples/platforms/simulation/system.c
@@ -36,19 +36,27 @@
#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0
+#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <getopt.h>
+#include <ifaddrs.h>
#include <libgen.h>
+#include <netinet/in.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
#include <openthread/tasklet.h>
#include <openthread/platform/alarm-milli.h>
#include <openthread/platform/radio.h>
+#include "simul_utils.h"
+#include "utils/code_utils.h"
+
uint32_t gNodeId = 1;
extern bool gPlatformPseudoResetWasRequested;
@@ -71,6 +79,7 @@
{
OT_SIM_OPT_HELP = 'h',
OT_SIM_OPT_ENABLE_ENERGY_SCAN = 'E',
+ OT_SIM_OPT_LOCAL_HOST = 'L',
OT_SIM_OPT_SLEEP_TO_TX = 't',
OT_SIM_OPT_TIME_SPEED = 's',
OT_SIM_OPT_LOG_FILE = 'l',
@@ -96,6 +105,56 @@
exit(aExitCode);
}
+static const char *GetLocalHostAddress(const char *aLocalHost)
+{
+ struct ifaddrs *ifaddr;
+ static char ipstr[INET_ADDRSTRLEN] = {0};
+ const char *rval = NULL;
+
+ {
+ struct in_addr addr;
+
+ otEXPECT_ACTION(inet_aton(aLocalHost, &addr) == 0, rval = aLocalHost);
+ }
+
+ if (getifaddrs(&ifaddr) == -1)
+ {
+ perror("getifaddrs");
+ exit(EXIT_FAILURE);
+ }
+
+ for (struct ifaddrs *ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
+ {
+ if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET)
+ {
+ continue;
+ }
+
+ if (strcmp(ifa->ifa_name, aLocalHost) == 0)
+ {
+ struct sockaddr_in *addr = (struct sockaddr_in *)ifa->ifa_addr;
+
+ if (inet_ntop(AF_INET, &addr->sin_addr, ipstr, sizeof(ipstr)))
+ {
+ break;
+ }
+ }
+ }
+
+ freeifaddrs(ifaddr);
+
+ if (ipstr[0] == '\0')
+ {
+ fprintf(stderr, "Local host address not found!\n");
+ exit(EXIT_FAILURE);
+ }
+
+ rval = ipstr;
+
+exit:
+ return rval;
+}
+
void otSysInit(int aArgCount, char *aArgVector[])
{
char *endptr;
@@ -106,6 +165,7 @@
{"enable-energy-scan", no_argument, 0, OT_SIM_OPT_ENABLE_ENERGY_SCAN},
{"sleep-to-tx", no_argument, 0, OT_SIM_OPT_SLEEP_TO_TX},
{"time-speed", required_argument, 0, OT_SIM_OPT_TIME_SPEED},
+ {"local-host", required_argument, 0, OT_SIM_OPT_LOCAL_HOST},
#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED)
{"log-file", required_argument, 0, OT_SIM_OPT_LOG_FILE},
#endif
@@ -113,9 +173,9 @@
};
#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED)
- static const char options[] = "Ehts:l:";
+ static const char options[] = "Ehts:L:l:";
#else
- static const char options[] = "Ehts:";
+ static const char options[] = "Ehts:L:";
#endif
if (gPlatformPseudoResetWasRequested)
@@ -149,6 +209,10 @@
case OT_SIM_OPT_SLEEP_TO_TX:
gRadioCaps |= OT_RADIO_CAPS_SLEEP_TO_TX;
break;
+ case OT_SIM_OPT_LOCAL_HOST:
+ gLocalHost = GetLocalHostAddress(optarg);
+ fprintf(stderr, "Simulate on %s\n", gLocalHost);
+ break;
case OT_SIM_OPT_TIME_SPEED:
speedUpFactor = (uint32_t)strtol(optarg, &endptr, 10);
if (*endptr != '\0' || speedUpFactor == 0)
@@ -189,6 +253,9 @@
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
platformTrelInit(speedUpFactor);
#endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+ platformInfraIfInit();
+#endif
platformRandomInit();
}
@@ -200,6 +267,9 @@
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
platformTrelDeinit();
#endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+ // platformInfrIfDeinit();
+#endif
platformLoggingDeinit();
}
@@ -222,6 +292,13 @@
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
platformTrelUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd);
#endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+ platformInfraIfUpdateFdSet(&read_fds, &write_fds, &max_fd);
+#endif
+
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+ platformBleUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd);
+#endif
if (otTaskletsArePending(aInstance))
{
@@ -235,6 +312,9 @@
{
platformUartProcess();
platformRadioProcess(aInstance, &read_fds, &write_fds);
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+ platformBleProcess(aInstance, &read_fds, &write_fds);
+#endif
}
else if (errno != EINTR)
{
@@ -246,6 +326,9 @@
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
platformTrelProcess(aInstance, &read_fds, &write_fds);
#endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+ platformInfraIfProcess(aInstance, &read_fds, &write_fds);
+#endif
if (gTerminate)
{
diff --git a/examples/platforms/simulation/trel.c b/examples/platforms/simulation/trel.c
index cfc34ac..696b1a5 100644
--- a/examples/platforms/simulation/trel.c
+++ b/examples/platforms/simulation/trel.c
@@ -31,6 +31,7 @@
#include <openthread/random_noncrypto.h>
#include <openthread/platform/trel.h>
+#include "simul_utils.h"
#include "utils/code_utils.h"
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
@@ -38,8 +39,6 @@
// Change DEBUG_LOG to all extra logging
#define DEBUG_LOG 0
-// The IPv4 group for receiving
-#define TREL_SIM_GROUP "224.0.0.116"
#define TREL_SIM_PORT 9200
#define TREL_MAX_PACKET_SIZE 1800
@@ -67,11 +66,9 @@
static uint8_t sNumPendingTx = 0;
static Message sPendingTx[TREL_MAX_PENDING_TX];
-static int sTxFd = -1;
-static int sRxFd = -1;
-static uint16_t sPortOffset = 0;
-static bool sEnabled = false;
-static uint16_t sUdpPort;
+static utilsSocket sSocket;
+static uint16_t sPortOffset = 0;
+static bool sEnabled = false;
static bool sServiceRegistered = false;
static uint16_t sServicePort;
@@ -117,80 +114,6 @@
}
#endif
-static void initFds(void)
-{
- int fd;
- int one = 1;
- struct sockaddr_in sockaddr;
- struct ip_mreqn mreq;
-
- memset(&sockaddr, 0, sizeof(sockaddr));
-
- otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sTxFd)"));
-
- sUdpPort = (uint16_t)(TREL_SIM_PORT + sPortOffset + gNodeId);
- sockaddr.sin_family = AF_INET;
- sockaddr.sin_port = htons(sUdpPort);
- sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
-
- otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &sockaddr.sin_addr, sizeof(sockaddr.sin_addr)) != -1,
- perror("setsockopt(sTxFd, IP_MULTICAST_IF)"));
-
- otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)) != -1,
- perror("setsockopt(sTxFd, IP_MULTICAST_LOOP)"));
-
- otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sTxFd)"));
-
- // Tx fd is successfully initialized.
- sTxFd = fd;
-
- otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sRxFd)"));
-
- otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != -1,
- perror("setsockopt(sRxFd, SO_REUSEADDR)"));
- otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != -1,
- perror("setsockopt(sRxFd, SO_REUSEPORT)"));
-
- memset(&mreq, 0, sizeof(mreq));
- inet_pton(AF_INET, TREL_SIM_GROUP, &mreq.imr_multiaddr);
-
- // Always use loopback device to send simulation packets.
- mreq.imr_address.s_addr = inet_addr("127.0.0.1");
-
- otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &mreq.imr_address, sizeof(mreq.imr_address)) != -1,
- perror("setsockopt(sRxFd, IP_MULTICAST_IF)"));
- otEXPECT_ACTION(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != -1,
- perror("setsockopt(sRxFd, IP_ADD_MEMBERSHIP)"));
-
- sockaddr.sin_family = AF_INET;
- sockaddr.sin_port = htons((uint16_t)(TREL_SIM_PORT + sPortOffset));
- sockaddr.sin_addr.s_addr = inet_addr(TREL_SIM_GROUP);
-
- otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sRxFd)"));
-
- // Rx fd is successfully initialized.
- sRxFd = fd;
-
-exit:
- if (sRxFd == -1 || sTxFd == -1)
- {
- exit(EXIT_FAILURE);
- }
-}
-
-static void deinitFds(void)
-{
- if (sRxFd != -1)
- {
- close(sRxFd);
- }
-
- if (sTxFd != -1)
- {
- close(sTxFd);
- }
-}
-
static uint16_t getMessageSize(const Message *aMessage)
{
return (uint16_t)(&aMessage->mData[aMessage->mDataLength] - (const uint8_t *)aMessage);
@@ -198,31 +121,13 @@
static void sendPendingTxMessages(void)
{
- ssize_t rval;
- struct sockaddr_in sockaddr;
-
- memset(&sockaddr, 0, sizeof(sockaddr));
- sockaddr.sin_family = AF_INET;
- inet_pton(AF_INET, TREL_SIM_GROUP, &sockaddr.sin_addr);
-
- sockaddr.sin_port = htons((uint16_t)(TREL_SIM_PORT + sPortOffset));
-
for (uint8_t i = 0; i < sNumPendingTx; i++)
{
- uint16_t size = getMessageSize(&sPendingTx[i]);
-
#if DEBUG_LOG
fprintf(stderr, "\r\n[trel-sim] Sending message (num:%d, type:%s, port:%u)\r\n", i,
messageTypeToString(sPendingTx[i].mType), sPendingTx[i].mSockAddr.mPort);
#endif
-
- rval = sendto(sTxFd, &sPendingTx[i], size, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
-
- if (rval < 0)
- {
- perror("sendto(sTxFd)");
- exit(EXIT_FAILURE);
- }
+ utilsSendOverSocket(&sSocket, &sPendingTx[i], getMessageSize(&sPendingTx[i]));
}
sNumPendingTx = 0;
@@ -279,7 +184,7 @@
switch (aMessage->mType)
{
case TREL_DATA_MESSAGE:
- otEXPECT(aMessage->mSockAddr.mPort == sUdpPort);
+ otEXPECT(aMessage->mSockAddr.mPort == sSocket.mPort);
otPlatTrelHandleReceived(aInstance, aMessage->mData, aMessage->mDataLength);
break;
@@ -309,7 +214,7 @@
{
OT_UNUSED_VARIABLE(aInstance);
- *aUdpPort = sUdpPort;
+ *aUdpPort = sSocket.mPort;
#if DEBUG_LOG
fprintf(stderr, "\r\n[trel-sim] otPlatTrelEnable() *aUdpPort=%u\r\n", *aUdpPort);
@@ -417,62 +322,46 @@
sPortOffset *= (MAX_NETWORK_SIZE + 1);
}
- initFds();
+ utilsInitSocket(&sSocket, TREL_SIM_PORT + sPortOffset);
OT_UNUSED_VARIABLE(aSpeedUpFactor);
}
-void platformTrelDeinit(void) { deinitFds(); }
+void platformTrelDeinit(void) { utilsDeinitSocket(&sSocket); }
void platformTrelUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd)
{
OT_UNUSED_VARIABLE(aTimeout);
// Always ready to receive
- if (aReadFdSet != NULL)
+ utilsAddSocketRxFd(&sSocket, aReadFdSet, aMaxFd);
+
+ if (sNumPendingTx > 0)
{
- FD_SET(sRxFd, aReadFdSet);
-
- if (aMaxFd != NULL && *aMaxFd < sRxFd)
- {
- *aMaxFd = sRxFd;
- }
- }
-
- if ((aWriteFdSet != NULL) && (sNumPendingTx > 0))
- {
- FD_SET(sTxFd, aWriteFdSet);
-
- if (aMaxFd != NULL && *aMaxFd < sTxFd)
- {
- *aMaxFd = sTxFd;
- }
+ utilsAddSocketTxFd(&sSocket, aWriteFdSet, aMaxFd);
}
}
void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
{
- if (FD_ISSET(sTxFd, aWriteFdSet) && (sNumPendingTx > 0))
+ if ((sNumPendingTx > 0) && utilsCanSocketSend(&sSocket, aWriteFdSet))
{
sendPendingTxMessages();
}
- if (FD_ISSET(sRxFd, aReadFdSet))
+ if (utilsCanSocketReceive(&sSocket, aReadFdSet))
{
- Message message;
- ssize_t rval;
+ Message message;
+ uint16_t len;
message.mDataLength = 0;
- rval = recvfrom(sRxFd, (char *)&message, sizeof(message), 0, NULL, NULL);
+ len = utilsReceiveFromSocket(&sSocket, &message, sizeof(message), NULL);
- if (rval < 0)
+ if (len > 0)
{
- perror("recvfrom(sRxFd)");
- exit(EXIT_FAILURE);
+ processMessage(aInstance, &message, len);
}
-
- processMessage(aInstance, &message, (uint16_t)(rval));
}
}
diff --git a/examples/platforms/simulation/uart.c b/examples/platforms/simulation/uart.c
index fe3fceb..70d3871 100644
--- a/examples/platforms/simulation/uart.c
+++ b/examples/platforms/simulation/uart.c
@@ -40,6 +40,7 @@
#include <openthread/platform/debug_uart.h>
+#include "simul_utils.h"
#include "utils/code_utils.h"
#include "utils/uart.h"
@@ -172,34 +173,13 @@
void platformUartUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int *aMaxFd)
{
- if (aReadFdSet != NULL)
+ utilsAddFdToFdSet(s_in_fd, aReadFdSet, aMaxFd);
+ utilsAddFdToFdSet(s_in_fd, aErrorFdSet, aMaxFd);
+
+ if ((s_write_length > 0))
{
- FD_SET(s_in_fd, aReadFdSet);
-
- if (aErrorFdSet != NULL)
- {
- FD_SET(s_in_fd, aErrorFdSet);
- }
-
- if (aMaxFd != NULL && *aMaxFd < s_in_fd)
- {
- *aMaxFd = s_in_fd;
- }
- }
-
- if ((aWriteFdSet != NULL) && (s_write_length > 0))
- {
- FD_SET(s_out_fd, aWriteFdSet);
-
- if (aErrorFdSet != NULL)
- {
- FD_SET(s_out_fd, aErrorFdSet);
- }
-
- if (aMaxFd != NULL && *aMaxFd < s_out_fd)
- {
- *aMaxFd = s_out_fd;
- }
+ utilsAddFdToFdSet(s_out_fd, aWriteFdSet, aMaxFd);
+ utilsAddFdToFdSet(s_out_fd, aErrorFdSet, aMaxFd);
}
}
diff --git a/examples/platforms/utils/mac_frame.cpp b/examples/platforms/utils/mac_frame.cpp
index 0ae88db..36a1c97 100644
--- a/examples/platforms/utils/mac_frame.cpp
+++ b/examples/platforms/utils/mac_frame.cpp
@@ -43,7 +43,7 @@
Mac::Address dst;
Mac::PanId panid;
- SuccessOrExit(frame.GetDstAddr(dst));
+ VerifyOrExit(frame.GetDstAddr(dst) == kErrorNone, rval = false);
switch (dst.GetType())
{
diff --git a/include/openthread/border_agent.h b/include/openthread/border_agent.h
index 1c7a535..33578e7 100644
--- a/include/openthread/border_agent.h
+++ b/include/openthread/border_agent.h
@@ -58,6 +58,30 @@
#define OT_BORDER_AGENT_ID_LENGTH (16)
/**
+ * Minimum length of the ephemeral key string.
+ *
+ */
+#define OT_BORDER_AGENT_MIN_EPHEMERAL_KEY_LENGTH (6)
+
+/**
+ * Maximum length of the ephemeral key string.
+ *
+ */
+#define OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_LENGTH (32)
+
+/**
+ * Default ephemeral key timeout interval in milliseconds.
+ *
+ */
+#define OT_BORDER_AGENT_DEFAULT_EPHEMERAL_KEY_TIMEOUT (2 * 60 * 1000u)
+
+/**
+ * Maximum ephemeral key timeout interval in milliseconds.
+ *
+ */
+#define OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_TIMEOUT (10 * 60 * 1000u)
+
+/**
* @struct otBorderAgentId
*
* Represents a Border Agent ID.
@@ -109,6 +133,8 @@
/**
* Gets the randomly generated Border Agent ID.
*
+ * Requires `OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE`.
+ *
* The ID is saved in persistent storage and survives reboots. The typical use case of the ID is to
* be published in the MeshCoP mDNS service as the `id` TXT value for the client to identify this
* Border Router/Agent device.
@@ -127,6 +153,8 @@
/**
* Sets the Border Agent ID.
*
+ * Requires `OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE`.
+ *
* The Border Agent ID will be saved in persistent storage and survive reboots. It's required to
* set the ID only once after factory reset. If the ID has never been set by calling this function,
* a random ID will be generated and returned when `otBorderAgentGetId` is called.
@@ -143,6 +171,112 @@
otError otBorderAgentSetId(otInstance *aInstance, const otBorderAgentId *aId);
/**
+ * Sets the ephemeral key for a given timeout duration.
+ *
+ * Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.
+ *
+ * The ephemeral key can be set when the Border Agent is already running and is not currently connected to any external
+ * commissioner (i.e., it is in `OT_BORDER_AGENT_STATE_STARTED` state). Otherwise `OT_ERROR_INVALID_STATE` is returned.
+ *
+ * The given @p aKeyString is directly used as the ephemeral PSK (excluding the trailing null `\0` character ).
+ * The @p aKeyString length must be between `OT_BORDER_AGENT_MIN_EPHEMERAL_KEY_LENGTH` and
+ * `OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_LENGTH`, inclusive.
+ *
+ * Setting the ephemeral key again before a previously set key has timed out will replace the previously set key and
+ * reset the timeout.
+ *
+ * While the timeout interval is in effect, the ephemeral key can be used only once by an external commissioner to
+ * connect. Once the commissioner disconnects, the ephemeral key is cleared, and the Border Agent reverts to using
+ * PSKc.
+ *
+ * @param[in] aInstance The OpenThread instance.
+ * @param[in] aKeyString The ephemeral key string (used as PSK excluding the trailing null `\0` character).
+ * @param[in] aTimeout The timeout duration in milliseconds to use the ephemeral key.
+ * If zero, the default `OT_BORDER_AGENT_DEFAULT_EPHEMERAL_KEY_TIMEOUT` value will be used.
+ * If the given timeout value is larger than `OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_TIMEOUT`, the
+ * max value `OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_TIMEOUT` will be used instead.
+ * @param[in] aUdpPort The UDP port to use with ephemeral key. If zero, an ephemeral port will be used.
+ * `otBorderAgentGetUdpPort()` will return the current UDP port being used.
+ *
+ * @retval OT_ERROR_NONE Successfully set the ephemeral key.
+ * @retval OT_ERROR_INVALID_STATE Border Agent is not running or it is connected to an external commissioner.
+ * @retval OT_ERROR_INVALID_ARGS The given @p aKeyString is not valid (too short or too long).
+ * @retval OT_ERROR_FAILED Failed to set the key (e.g., could not bind to UDP port).
+
+ *
+ */
+otError otBorderAgentSetEphemeralKey(otInstance *aInstance,
+ const char *aKeyString,
+ uint32_t aTimeout,
+ uint16_t aUdpPort);
+
+/**
+ * Cancels the ephemeral key that is in use.
+ *
+ * Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.
+ *
+ * Can be used to cancel a previously set ephemeral key before it times out. If the Border Agent is not running or
+ * there is no ephemeral key in use, calling this function has no effect.
+ *
+ * If a commissioner is connected using the ephemeral key and is currently active, calling this function does not
+ * change its state. In this case the `otBorderAgentIsEphemeralKeyActive()` will continue to return `TRUE` until the
+ * commissioner disconnects.
+ *
+ * @param[in] aInstance The OpenThread instance.
+ *
+ */
+void otBorderAgentClearEphemeralKey(otInstance *aInstance);
+
+/**
+ * Indicates whether or not an ephemeral key is currently active.
+ *
+ * Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.
+ *
+ * @param[in] aInstance The OpenThread instance.
+ *
+ * @retval TRUE An ephemeral key is active.
+ * @retval FALSE No ephemeral key is active.
+ *
+ */
+bool otBorderAgentIsEphemeralKeyActive(otInstance *aInstance);
+
+/**
+ * Callback function pointer to signal changes related to the Border Agent's ephemeral key.
+ *
+ * This callback is invoked whenever:
+ *
+ * - The Border Agent starts using an ephemeral key.
+ * - Any parameter related to the ephemeral key, such as the port number, changes.
+ * - The Border Agent stops using the ephemeral key due to:
+ * - A direct call to `otBorderAgentClearEphemeralKey()`.
+ * - The ephemeral key timing out.
+ * - An external commissioner successfully using the key to connect and then disconnecting.
+ * - Reaching the maximum number of allowed failed connection attempts.
+ *
+ * Any OpenThread API, including `otBorderAgent` APIs, can be safely called from this callback.
+ *
+ * @param[in] aContext A pointer to an arbitrary context (provided when callback is set).
+ *
+ */
+typedef void (*otBorderAgentEphemeralKeyCallback)(void *aContext);
+
+/**
+ * Sets the callback function used by the Border Agent to notify any changes related to use of ephemeral key.
+ *
+ * Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.
+ *
+ * A subsequent call to this function will replace any previously set callback.
+ *
+ * @param[in] aInstance The OpenThread instance.
+ * @param[in] aCallback The callback function pointer.
+ * @param[in] aContext The arbitrary context to use with callback.
+ *
+ */
+void otBorderAgentSetEphemeralKeyCallback(otInstance *aInstance,
+ otBorderAgentEphemeralKeyCallback aCallback,
+ void *aContext);
+
+/**
* @}
*
*/
diff --git a/include/openthread/border_routing.h b/include/openthread/border_routing.h
index afd2d7e..1e5ff61 100644
--- a/include/openthread/border_routing.h
+++ b/include/openthread/border_routing.h
@@ -240,6 +240,22 @@
void otBorderRoutingClearRouteInfoOptionPreference(otInstance *aInstance);
/**
+ * Sets additional options to append at the end of emitted Router Advertisement (RA) messages.
+ *
+ * The content of @p aOptions is copied internally, so it can be a temporary buffer (e.g., a stack allocated array).
+ *
+ * Subsequent calls to this function overwrite the previously set value.
+ *
+ * @param[in] aOptions A pointer to the encoded options. Can be `NULL` to clear.
+ * @param[in] aLength Number of bytes in @p aOptions.
+ *
+ * @retval OT_ERROR_NONE Successfully set the extra option bytes.
+ * @retval OT_ERROR_NO_BUFS Could not allocate buffer to save the buffer.
+ *
+ */
+otError otBorderRoutingSetExtraRouterAdvertOptions(otInstance *aInstance, const uint8_t *aOptions, uint16_t aLength);
+
+/**
* Gets the current preference used for published routes in Network Data.
*
* The preference is determined as follows:
diff --git a/include/openthread/channel_manager.h b/include/openthread/channel_manager.h
index 6afe3a4..e024957 100644
--- a/include/openthread/channel_manager.h
+++ b/include/openthread/channel_manager.h
@@ -47,8 +47,14 @@
* @brief
* This module includes functions for Channel Manager.
*
- * The functions in this module are available when Channel Manager feature
- * (`OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE`) is enabled. Channel Manager is available only on an FTD build.
+ * The functions in this module are available when Channel Manager features
+ * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE` are enabled. Channel Manager behavior depends on the
+ * device role. It manages the network-wide PAN channel on a Full Thread Device in rx-on-when-idle mode, or with
+ * `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE` set,
+ * selects CSL channel in synchronized rx-off-when-idle mode. On a Minimal Thread Device
+ * `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE` selects
+ * the CSL channel.
*
* @{
*
@@ -77,7 +83,9 @@
uint8_t otChannelManagerGetRequestedChannel(otInstance *aInstance);
/**
- * Gets the delay (in seconds) used by Channel Manager for a channel change.
+ * Gets the delay (in seconds) used by Channel Manager for a network channel change.
+ *
+ * Only available on FTDs.
*
* @param[in] aInstance A pointer to an OpenThread instance.
*
@@ -87,10 +95,10 @@
uint16_t otChannelManagerGetDelay(otInstance *aInstance);
/**
- * Sets the delay (in seconds) used for a channel change.
+ * Sets the delay (in seconds) used for a network channel change.
*
- * The delay should preferably be longer than the maximum data poll interval used by all sleepy-end-devices within the
- * Thread network.
+ * Only available on FTDs. The delay should preferably be longer than the maximum data poll interval used by all
+ * Sleepy End Devices within the Thread network.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aDelay Delay in seconds.
@@ -117,7 +125,7 @@
*
* 2) If the first step passes, then `ChannelManager` selects a potentially better channel. It uses the collected
* channel quality data by `ChannelMonitor` module. The supported and favored channels are used at this step.
- * (see otChannelManagerSetSupportedChannels() and otChannelManagerSetFavoredChannels()).
+ * (see `otChannelManagerSetSupportedChannels()` and `otChannelManagerSetFavoredChannels()`).
*
* 3) If the newly selected channel is different from the current channel, `ChannelManager` requests/starts the
* channel change process (internally invoking a `RequestChannelChange()`).
@@ -132,10 +140,41 @@
otError otChannelManagerRequestChannelSelect(otInstance *aInstance, bool aSkipQualityCheck);
/**
- * Enables or disables the auto-channel-selection functionality.
+ * Requests that `ChannelManager` checks and selects a new CSL channel and starts a CSL channel change.
+ *
+ * Only available with `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`. This function asks the `ChannelManager` to select a
+ * channel by itself (based on collected channel quality info).
+ *
+ * Once called, the Channel Manager will perform the following 3 steps:
+ *
+ * 1) `ChannelManager` decides if the CSL channel change would be helpful. This check can be skipped if
+ * `aSkipQualityCheck` is set to true (forcing a CSL channel selection to happen and skipping the quality check).
+ * This step uses the collected link quality metrics on the device (such as CCA failure rate, frame and message
+ * error rates per neighbor, etc.) to determine if the current channel quality is at the level that justifies
+ * a CSL channel change.
+ *
+ * 2) If the first step passes, then `ChannelManager` selects a potentially better CSL channel. It uses the collected
+ * channel quality data by `ChannelMonitor` module. The supported and favored channels are used at this step.
+ * (see `otChannelManagerSetSupportedChannels()` and `otChannelManagerSetFavoredChannels()`).
+ *
+ * 3) If the newly selected CSL channel is different from the current CSL channel, `ChannelManager` starts the
+ * CSL channel change process.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ * @param[in] aSkipQualityCheck Indicates whether the quality check (step 1) should be skipped.
+ *
+ * @retval OT_ERROR_NONE Channel selection finished successfully.
+ * @retval OT_ERROR_NOT_FOUND Supported channel mask is empty, therefore could not select a channel.
+ *
+ */
+otError otChannelManagerRequestCslChannelSelect(otInstance *aInstance, bool aSkipQualityCheck);
+
+/**
+ * Enables or disables the auto-channel-selection functionality for network channel.
*
* When enabled, `ChannelManager` will periodically invoke a `RequestChannelSelect(false)`. The period interval
- * can be set by `SetAutoChannelSelectionInterval()`.
+ * can be set by `otChannelManagerSetAutoChannelSelectionInterval()`.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aEnabled Indicates whether to enable or disable this functionality.
@@ -144,7 +183,7 @@
void otChannelManagerSetAutoChannelSelectionEnabled(otInstance *aInstance, bool aEnabled);
/**
- * Indicates whether the auto-channel-selection functionality is enabled or not.
+ * Indicates whether the auto-channel-selection functionality for a network channel is enabled or not.
*
* @param[in] aInstance A pointer to an OpenThread instance.
*
@@ -154,6 +193,33 @@
bool otChannelManagerGetAutoChannelSelectionEnabled(otInstance *aInstance);
/**
+ * Enables or disables the auto-channel-selection functionality for a CSL channel.
+ *
+ * Only available with `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`. When enabled, `ChannelManager` will periodically invoke
+ * a `otChannelManagerRequestCslChannelSelect()`. The period interval can be set by
+ * `otChannelManagerSetAutoChannelSelectionInterval()`.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ * @param[in] aEnabled Indicates whether to enable or disable this functionality.
+ *
+ */
+void otChannelManagerSetAutoCslChannelSelectionEnabled(otInstance *aInstance, bool aEnabled);
+
+/**
+ * Indicates whether the auto-csl-channel-selection functionality is enabled or not.
+ *
+ * Only available with `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`.
+ *
+ * @param[in] aInstance A pointer to an OpenThread instance.
+ *
+ * @returns TRUE if enabled, FALSE if disabled.
+ *
+ */
+bool otChannelManagerGetAutoCslChannelSelectionEnabled(otInstance *aInstance);
+
+/**
* Sets the period interval (in seconds) used by auto-channel-selection functionality.
*
* @param[in] aInstance A pointer to an OpenThread instance.
diff --git a/include/openthread/instance.h b/include/openthread/instance.h
index 546b5d1..1ce766c 100644
--- a/include/openthread/instance.h
+++ b/include/openthread/instance.h
@@ -53,7 +53,7 @@
* @note This number versions both OpenThread platform and user APIs.
*
*/
-#define OPENTHREAD_API_VERSION (397)
+#define OPENTHREAD_API_VERSION (402)
/**
* @addtogroup api-instance
diff --git a/include/openthread/ip6.h b/include/openthread/ip6.h
index 7f54b73..2636702 100644
--- a/include/openthread/ip6.h
+++ b/include/openthread/ip6.h
@@ -236,7 +236,6 @@
otIp6Address mPeerAddr; ///< The peer IPv6 address.
uint16_t mSockPort; ///< The local transport-layer port.
uint16_t mPeerPort; ///< The peer transport-layer port.
- const void *mLinkInfo; ///< A pointer to link-specific information.
uint8_t mHopLimit; ///< The IPv6 Hop Limit value. Only applies if `mAllowZeroHopLimit` is FALSE.
///< If `0`, IPv6 Hop Limit is default value `OPENTHREAD_CONFIG_IP6_HOP_LIMIT_DEFAULT`.
///< Otherwise, specifies the IPv6 Hop Limit.
diff --git a/include/openthread/link.h b/include/openthread/link.h
index f4d203e..15bc0d8 100644
--- a/include/openthread/link.h
+++ b/include/openthread/link.h
@@ -55,27 +55,6 @@
#define OT_US_PER_TEN_SYMBOLS OT_RADIO_TEN_SYMBOLS_TIME ///< Time for 10 symbols in units of microseconds
/**
- * Represents link-specific information for messages received from the Thread radio.
- *
- */
-typedef struct otThreadLinkInfo
-{
- uint16_t mPanId; ///< Source PAN ID
- uint8_t mChannel; ///< 802.15.4 Channel
- int8_t mRss; ///< Received Signal Strength in dBm.
- uint8_t mLqi; ///< Link Quality Indicator for a received message.
- bool mLinkSecurity : 1; ///< Indicates whether or not link security is enabled.
- bool mIsDstPanIdBroadcast : 1; ///< Indicates whether or not destination PAN ID is broadcast.
-
- // Applicable/Required only when time sync feature (`OPENTHREAD_CONFIG_TIME_SYNC_ENABLE`) is enabled.
- uint8_t mTimeSyncSeq; ///< The time sync sequence.
- int64_t mNetworkTimeOffset; ///< The time offset to the Thread network time, in microseconds.
-
- // Applicable only when OPENTHREAD_CONFIG_MULTI_RADIO feature is enabled.
- uint8_t mRadioType; ///< Radio link type.
-} otThreadLinkInfo;
-
-/**
* Used to indicate no fixed received signal strength was set
*
*/
diff --git a/include/openthread/message.h b/include/openthread/message.h
index 3c2938c..39e14d4 100644
--- a/include/openthread/message.h
+++ b/include/openthread/message.h
@@ -91,6 +91,27 @@
} otMessageSettings;
/**
+ * Represents link-specific information for messages received from the Thread radio.
+ *
+ */
+typedef struct otThreadLinkInfo
+{
+ uint16_t mPanId; ///< Source PAN ID
+ uint8_t mChannel; ///< 802.15.4 Channel
+ int8_t mRss; ///< Received Signal Strength in dBm (averaged over fragments)
+ uint8_t mLqi; ///< Average Link Quality Indicator (averaged over fragments)
+ bool mLinkSecurity : 1; ///< Indicates whether or not link security is enabled.
+ bool mIsDstPanIdBroadcast : 1; ///< Indicates whether or not destination PAN ID is broadcast.
+
+ // Applicable/Required only when time sync feature (`OPENTHREAD_CONFIG_TIME_SYNC_ENABLE`) is enabled.
+ uint8_t mTimeSyncSeq; ///< The time sync sequence.
+ int64_t mNetworkTimeOffset; ///< The time offset to the Thread network time, in microseconds.
+
+ // Applicable only when OPENTHREAD_CONFIG_MULTI_RADIO feature is enabled.
+ uint8_t mRadioType; ///< Radio link type.
+} otThreadLinkInfo;
+
+/**
* Free an allocated message buffer.
*
* @param[in] aMessage A pointer to a message buffer.
@@ -266,12 +287,26 @@
/**
* Returns the average RSS (received signal strength) associated with the message.
*
+ * @param[in] aMessage A pointer to a message buffer.
+ *
* @returns The average RSS value (in dBm) or OT_RADIO_RSSI_INVALID if no average RSS is available.
*
*/
int8_t otMessageGetRss(const otMessage *aMessage);
/**
+ * Retrieves the link-specific information for a message received over Thread radio.
+ *
+ * @param[in] aMessage The message from which to retrieve `otThreadLinkInfo`.
+ * @pram[out] aLinkInfo A pointer to an `otThreadLinkInfo` to populate.
+ *
+ * @retval OT_ERROR_NONE Successfully retrieved the link info, @p `aLinkInfo` is updated.
+ * @retval OT_ERROR_NOT_FOUND Message origin is not `OT_MESSAGE_ORIGIN_THREAD_NETIF`.
+ *
+ */
+otError otMessageGetThreadLinkInfo(const otMessage *aMessage, otThreadLinkInfo *aLinkInfo);
+
+/**
* Append bytes to a message.
*
* @param[in] aMessage A pointer to a message buffer.
diff --git a/include/openthread/thread.h b/include/openthread/thread.h
index b077001..4d814c1 100644
--- a/include/openthread/thread.h
+++ b/include/openthread/thread.h
@@ -709,7 +709,7 @@
* @sa otThreadSetKeySwitchGuardTime
*
*/
-uint32_t otThreadGetKeySwitchGuardTime(otInstance *aInstance);
+uint16_t otThreadGetKeySwitchGuardTime(otInstance *aInstance);
/**
* Sets the thrKeySwitchGuardTime (in hours).
@@ -723,7 +723,7 @@
* @sa otThreadGetKeySwitchGuardTime
*
*/
-void otThreadSetKeySwitchGuardTime(otInstance *aInstance, uint32_t aKeySwitchGuardTime);
+void otThreadSetKeySwitchGuardTime(otInstance *aInstance, uint16_t aKeySwitchGuardTime);
/**
* Detach from the Thread network.
diff --git a/script/cmake-build b/script/cmake-build
index 5ed2e4e..f8e227c 100755
--- a/script/cmake-build
+++ b/script/cmake-build
@@ -109,6 +109,7 @@
"-DOT_SRP_CLIENT=ON"
"-DOT_SRP_SERVER=ON"
"-DOT_UPTIME=ON"
+ "-DOT_BLE_TCAT=ON"
)
readonly OT_POSIX_SIM_COMMON_OPTIONS
diff --git a/script/make-pretty b/script/make-pretty
index 491d4ac..a0921af 100755
--- a/script/make-pretty
+++ b/script/make-pretty
@@ -95,6 +95,7 @@
'-DOT_BORDER_ROUTING=ON'
'-DOT_BORDER_ROUTING_DHCP6_PD=ON'
'-DOT_CHANNEL_MANAGER=ON'
+ '-DOT_CHANNEL_MANAGER_CSL=ON'
'-DOT_CHANNEL_MONITOR=ON'
'-DOT_COAP=ON'
'-DOT_COAP_BLOCK=ON'
diff --git a/src/cli/BUILD.gn b/src/cli/BUILD.gn
index 318a4d0..b74577a 100644
--- a/src/cli/BUILD.gn
+++ b/src/cli/BUILD.gn
@@ -55,8 +55,6 @@
"cli_mac_filter.hpp",
"cli_network_data.cpp",
"cli_network_data.hpp",
- "cli_output.cpp",
- "cli_output.hpp",
"cli_ping.cpp",
"cli_ping.hpp",
"cli_srp_client.cpp",
@@ -67,6 +65,8 @@
"cli_tcp.hpp",
"cli_udp.cpp",
"cli_udp.hpp",
+ "cli_utils.cpp",
+ "cli_utils.hpp",
"x509_cert_key.hpp",
]
diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt
index f36439f..9dfee40 100644
--- a/src/cli/CMakeLists.txt
+++ b/src/cli/CMakeLists.txt
@@ -45,13 +45,13 @@
cli_link_metrics.cpp
cli_mac_filter.cpp
cli_network_data.cpp
- cli_output.cpp
cli_ping.cpp
cli_srp_client.cpp
cli_srp_server.cpp
cli_tcat.cpp
cli_tcp.cpp
cli_udp.cpp
+ cli_utils.cpp
)
set(OT_CLI_VENDOR_EXTENSION "" CACHE STRING "Path to CMake file to define and link cli vendor extension")
diff --git a/src/cli/README.md b/src/cli/README.md
index 262b9f5..c8dfe27 100644
--- a/src/cli/README.md
+++ b/src/cli/README.md
@@ -86,6 +86,7 @@
- [networkkey](#networkkey)
- [networkname](#networkname)
- [networktime](#networktime)
+- [nexthop](#nexthop)
- [panid](#panid)
- [parent](#parent)
- [parentpriority](#parentpriority)
@@ -357,12 +358,99 @@
Print border agent state.
+Possible states are
+
+- `Stopped` : Border Agent is stopped.
+- `Started` : Border Agent is running with no active connection with external commissioner.
+- `Active` : Border Agent is running and is connected with an external commissioner.
+
```bash
> ba state
Started
Done
```
+### ba ephemeralkey
+
+Indicates if an ephemeral key is active.
+
+Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.
+
+```bash
+> ba ephemeralkey
+inactive
+Done
+
+> ba ephemeralkey set Z10X20g3J15w1000P60m16 1000
+Done
+
+> ba ephemeralkey
+active
+Done
+```
+
+### ba ephemeralkey set \<keystring\> \[timeout\] \[port\]
+
+Sets the ephemeral key for a given timeout duration.
+
+Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.
+
+The ephemeral key can be set when Border Agent is already running and is not currently connected to any external commissioner (i.e., `ba state` gives `Started`).
+
+The `keystring` string is directly used as the ephemeral PSK (excluding the trailing null `\0` character). Its length MUST be between 6 and 32, inclusive.
+
+The `timeout` is in milliseconds. If not provided or set to zero, the default value of 2 minutes will be used. If the timeout value is larger than 10 minutes, the 10 minutes timeout value will be used instead.
+
+The `port` specifies the UDP port to use with the ephemeral key. If UDP port is zero or is not provided, an ephemeral port will be used. `ba port` will give the current UDP port in use by the Border Agent.
+
+Setting the ephemeral key again before a previously set one is timed out, will replace the previous one.
+
+While the timeout interval is in effect, the ephemeral key can be used only once by an external commissioner to connect. Once the commissioner disconnects, the ephemeral key is cleared, and Border Agent reverts to using PSKc.
+
+```bash
+> ba ephemeralkey set Z10X20g3J15w1000P60m16 5000 1234
+Done
+```
+
+### ba ephemeralkey clear
+
+Cancels the ephemeral key in use if any.
+
+Requires `OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE`.
+
+Can be used to cancel a previously set ephemeral key before it is used or times out. If the Border Agent is not running or there is no ephemeral key in use, calling this function has no effect.
+
+If a commissioner is connected using the ephemeral key and is currently active, calling this method does not change its state. In this case the `ba ephemeralkey` will continue to return `active` until the commissioner disconnects.
+
+```bash
+> ba ephemeralkey clear
+Done
+```
+
+### ba ephemeralkey callback enable
+
+Enables callback from Border Agent for ephemeral key state changes.
+
+```bash
+> ba ephemeralkey callback enable
+Done
+
+> ba ephemeralkey set W10X12 5000 49155
+Done
+
+BorderAgent callback: Ephemeral key active, port:49155
+BorderAgent callback: Ephemeral key inactive
+```
+
+### ba ephemeralkey callback disable
+
+Disables callback from Border Agent for ephemeral key state changes.
+
+```bash
+> ba ephemeralkey callback disable
+Done
+```
+
### bufferinfo
Show the current message buffer information.
@@ -1026,30 +1114,6 @@
Done
```
-### networktime
-
-Get the Thread network time and the time sync parameters.
-
-```bash
-> networktime
-Network Time: 21084154us (synchronized)
-Time Sync Period: 100s
-XTAL Threshold: 300ppm
-Done
-```
-
-### networktime \<timesyncperiod\> \<xtalthreshold\>
-
-Set time sync parameters
-
-- timesyncperiod: The time synchronization period, in seconds.
-- xtalthreshold: The XTAL accuracy threshold for a device to become Router-Capable device, in PPM.
-
-```bash
-> networktime 100 300
-Done
-```
-
### debug
Executes a series of CLI commands to gather information about the device and thread network. This is intended for debugging.
@@ -1762,6 +1826,8 @@
Set the Thread Key Sequence Counter.
+This command is reserved for testing and demo purposes only. Changing Key Sequence Counter will render a production application non-compliant with the Thread Specification.
+
```bash
> keysequence counter 10
Done
@@ -1779,7 +1845,9 @@
### keysequence guardtime \<guardtime\>
-Set Thread Key Switch Guard Time (in hours) 0 means Thread Key Switch immediately if key index match
+Set Thread Key Switch Guard Time (in hours).
+
+This command is reserved for testing and demo purposes only. Changing Key Switch Guard Time will render a production application non-compliant with the Thread Specification.
```bash
> keysequence guardtime 0
@@ -2696,6 +2764,61 @@
Done
```
+### networktime
+
+Get the Thread network time and the time sync parameters.
+
+```bash
+> networktime
+Network Time: 21084154us (synchronized)
+Time Sync Period: 100s
+XTAL Threshold: 300ppm
+Done
+```
+
+### networktime \<timesyncperiod\> \<xtalthreshold\>
+
+Set time sync parameters
+
+- timesyncperiod: The time synchronization period, in seconds.
+- xtalthreshold: The XTAL accuracy threshold for a device to become Router-Capable device, in PPM.
+
+```bash
+> networktime 100 300
+Done
+```
+
+### nexthop
+
+Output the table of allocated Router IDs and the current next hop (as Router ID) and path cost for each ID.
+
+```bash
+> nexthop
+| ID |NxtHop| Cost |
++------+------+------+
+| 9 | 9 | 1 |
+| 25 | 25 | 0 |
+| 30 | 30 | 1 |
+| 46 | - | - |
+| 50 | 30 | 3 |
+| 60 | 30 | 2 |
+Done
+```
+
+### nexthop \<rloc16\>
+
+Get the next hop (as RLOC16) and path cost towards a given RLOC16 destination.
+
+```bash
+> nexthop 0xc000
+0xc000 cost:0
+Done
+
+nexthop 0x8001
+0x2000 cost:3
+Done
+```
+
### panid
Get the IEEE 802.15.4 PAN ID value.
diff --git a/src/cli/README_BR.md b/src/cli/README_BR.md
index 189de8a..8376290 100644
--- a/src/cli/README_BR.md
+++ b/src/cli/README_BR.md
@@ -36,6 +36,7 @@
onlinkprefix
pd
prefixtable
+raoptions
rioprf
routeprf
routers
@@ -235,6 +236,28 @@
Done
```
+### raoptions
+
+Usage: `br raoptions <options>`
+
+Sets additional options to append at the end of emitted Router Advertisement (RA) messages. `<options>` provided as hex bytes.
+
+```bash
+> br raoptions 0400ff00020001
+Done
+```
+
+### raoptions clear
+
+Usage: `br raoptions clear`
+
+Clear any previously set additional options to append at the end of emitted Router Advertisement (RA) messages.
+
+```bash
+> br raoptions clear
+Done
+```
+
### rioprf
Usage: `br rioprf`
diff --git a/src/cli/README_DATASET.md b/src/cli/README_DATASET.md
index 54f4c1e..1f32cce 100644
--- a/src/cli/README_DATASET.md
+++ b/src/cli/README_DATASET.md
@@ -107,6 +107,58 @@
Done
```
+### Using the Dataset Updater to update Operational Dataset
+
+Dataset Updater can be used for a delayed update of network parameters on all devices of a Thread Network.
+
+1. Clear the dataset buffer and add the Dataset fields to update.
+
+ ```bash
+ > dataset clear
+ Done
+
+ > dataset channel 12
+ Done
+ ```
+
+2. Set the delay timer parameter (example uses 5 minutes or 300000 ms). Check the resulting dataset. There is no need to specify active or pending timestamps because the Dataset Updater will handle this. If specified the `dataset updater start` will issue an error.
+
+ ```bash
+ > dataset delay 300000
+
+ > dataset
+ Channel: 12
+ Delay: 30000
+ Done
+ ```
+
+3. Start the Dataset Updater, which will prepare a Pending Operation Dataset and inform the Leader to distribute it to other devices.
+
+ ```bash
+ > dataset updater start
+ Done
+
+ > dataset updater
+ Enabled
+ ```
+
+4. After about 5 minutes, the changes are applied to the Active Operational Dataset on the Leader. This can also be checked at other devices on the network: these should have applied the new Dataset too, at approximately the same time as the Leader has done this.
+
+ ```bash
+ > dataset active
+ Active Timestamp: 10
+ Channel: 12
+ Channel Mask: 0x07fff800
+ Ext PAN ID: 324a71d90cdc8345
+ Mesh Local Prefix: fd7d:da74:df5e:80c::/64
+ Network Key: be768535bac1b8d228960038311d6ca2
+ Network Name: OpenThread-bcaf
+ PAN ID: 0xbcaf
+ PSKc: e79b274ab22414a814ed5cce6a30be67
+ Security Policy: 672 onrc 0
+ Done
+ ```
+
### Using the Pending Operational Dataset for Delayed Dataset Updates
The Pending Operational Dataset can be used for a delayed update of network parameters on all devices of a Thread Network. If certain Active Operational Dataset parameters need to be changed, but the change would impact the connectivity of the network, delaying the update helps to let all devices receive the new parameters before the update is applied. Examples of such parameters are the channel, PAN ID, certain Security Policy bits, or Network Key.
@@ -241,6 +293,7 @@
- [pskc](#pskc)
- [securitypolicy](#securitypolicy)
- [tlvs](#tlvs)
+- [updater](#updater)
## Command Details
@@ -695,3 +748,74 @@
0e080000000000010000000300001635060004001fffe00208d196fa2040e973b60708fdbbc310c48f3a3905109929154dbc363218bcd22f907caf5c15030f4f70656e5468726561642d646532620102de2b041015b2c16f7ba92ed4bc7b1ee054f1553f0c0402a0f7f8
Done
```
+
+### updater
+
+Usage: `dataset updater`
+
+Requires `OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE`.
+
+Indicate whether there is an ongoing Operation Dataset update request.
+
+```bash
+> dataset updater
+Enabled
+```
+
+### updater start
+
+Usage: `dataset updater start`
+
+Requires `OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE`.
+
+Request network to update its Operation Dataset to the current operational dataset buffer.
+
+The current operational dataset buffer should contain the fields to be updated with their new values. It must not contain Active or Pending Timestamp fields. The Delay field is optional. If not provided, a default value (1000 ms) is used.
+
+```bash
+> channel
+19
+Done
+
+> dataset clear
+Done
+> dataset channel 15
+Done
+> dataset
+Channel: 15
+Done
+
+> dataset updater start
+Done
+> dataset updater
+Enabled
+Done
+
+Dataset update complete: OK
+
+> channel
+15
+Done
+```
+
+### updater cancel
+
+Usage: `dataset updater cancel`
+
+Requires `OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE`.
+
+Cancels an ongoing (if any) Operational Dataset update request.
+
+```bash
+> dataset updater start
+Done
+> dataset updater
+Enabled
+Done
+>
+> dataset updater cancel
+Done
+> dataset updater
+Disabled
+Done
+```
diff --git a/src/cli/README_NETDATA.md b/src/cli/README_NETDATA.md
index ad33bb9..9402885 100644
--- a/src/cli/README_NETDATA.md
+++ b/src/cli/README_NETDATA.md
@@ -334,10 +334,12 @@
### show
-Usage: `netdata show [local] [-x]`
+Usage: `netdata show [local] [-x] [\<rloc16\>]`
Print entries in Network Data, on-mesh prefixes, external routes, services, and 6LoWPAN context information.
+If the optional `rloc16` input is specified, prints the entries associated with the given RLOC16 only. The RLOC16 filtering can be used when `-x` or `local` are not used.
+
On-mesh prefixes are listed under `Prefixes` header:
- The on-mesh prefix
@@ -406,6 +408,19 @@
Done
```
+Print Network Data entries from the Leader associated with `0xa00` RLOC16.
+
+```bash
+> netdata show 0xa00
+Prefixes:
+fd00:dead:beef:cafe::/64 paros med a000
+Routes:
+fd00:1234:0:0::/64 s med a000
+Services:
+44970 5d fddead00beef00007bad0069ce45948504d2 s a000
+Done
+```
+
Print Network Data received from the Leader as hex-encoded TLVs.
```bash
diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp
index 4d1ab02..33d1123 100644
--- a/src/cli/cli.cpp
+++ b/src/cli/cli.cpp
@@ -68,7 +68,9 @@
#include <openthread/backbone_router_ftd.h>
#endif
#endif
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+ (OPENTHREAD_FTD || \
+ (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
#include <openthread/channel_manager.h>
#endif
#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
@@ -99,7 +101,7 @@
Interpreter::Interpreter(Instance *aInstance, otCliOutputCallback aCallback, void *aContext)
: OutputImplementer(aCallback, aContext)
- , Output(aInstance, *this)
+ , Utils(aInstance, *this)
, mCommandIsPending(false)
, mInternalDebugCommand(false)
, mTimer(*aInstance, HandleTimer, this)
@@ -337,7 +339,7 @@
VerifyOrExit(StringLength(aBuf, kMaxLineLength) <= kMaxLineLength - 1, error = OT_ERROR_PARSE);
}
- SuccessOrExit(error = Utils::CmdLineParser::ParseCmd(aBuf, args, kMaxArgs));
+ SuccessOrExit(error = ot::Utils::CmdLineParser::ParseCmd(aBuf, args, kMaxArgs));
VerifyOrExit(!args[0].IsEmpty(), mCommandIsPending = false);
if (!mInternalDebugCommand)
@@ -408,26 +410,6 @@
return error;
}
-otError Interpreter::ParseEnableOrDisable(const Arg &aArg, bool &aEnable)
-{
- otError error = OT_ERROR_NONE;
-
- if (aArg == "enable")
- {
- aEnable = true;
- }
- else if (aArg == "disable")
- {
- aEnable = false;
- }
- else
- {
- error = OT_ERROR_INVALID_COMMAND;
- }
-
- return error;
-}
-
#if OPENTHREAD_FTD || OPENTHREAD_MTD
otError Interpreter::ParseJoinerDiscerner(Arg &aArg, otJoinerDiscerner &aDiscerner)
@@ -441,7 +423,7 @@
VerifyOrExit(separator != nullptr, error = OT_ERROR_NOT_FOUND);
- SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(separator + 1, aDiscerner.mLength));
+ SuccessOrExit(error = ot::Utils::CmdLineParser::ParseAsUint8(separator + 1, aDiscerner.mLength));
VerifyOrExit(aDiscerner.mLength > 0 && aDiscerner.mLength <= 64, error = OT_ERROR_INVALID_ARGS);
*separator = '\0';
error = aArg.ParseAsUint64(aDiscerner.mValue);
@@ -610,6 +592,98 @@
}
}
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+ else if (aArgs[0] == "ephemeralkey")
+ {
+ /**
+ * @cli ba ephemeralkey
+ * @code
+ * ba ephemeralkey
+ * active
+ * Done
+ * @endcode
+ * @par api_copy
+ * #otBorderAgentIsEphemeralKeyActive
+ */
+ if (aArgs[1].IsEmpty())
+ {
+ OutputLine("%sactive", otBorderAgentIsEphemeralKeyActive(GetInstancePtr()) ? "" : "in");
+ }
+ /**
+ * @cli ba ephemeralkey set <keystring> [timeout-in-msec] [port]
+ * @code
+ * ba ephemeralkey set Z10X20g3J15w1000P60m16 5000 1234
+ * Done
+ * @endcode
+ * @par api_copy
+ * #otBorderAgentSetEphemeralKey
+ */
+ else if (aArgs[1] == "set")
+ {
+ uint32_t timeout = 0;
+ uint16_t port = 0;
+
+ VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+
+ if (!aArgs[3].IsEmpty())
+ {
+ SuccessOrExit(error = aArgs[3].ParseAsUint32(timeout));
+ }
+
+ if (!aArgs[4].IsEmpty())
+ {
+ SuccessOrExit(error = aArgs[4].ParseAsUint16(port));
+ }
+
+ error = otBorderAgentSetEphemeralKey(GetInstancePtr(), aArgs[2].GetCString(), timeout, port);
+ }
+ /**
+ * @cli ba ephemeralkey clear
+ * @code
+ * ba ephemeralkey clear
+ * Done
+ * @endcode
+ * @par api_copy
+ * #otBorderAgentClearEphemeralKey
+ */
+ else if (aArgs[1] == "clear")
+ {
+ otBorderAgentClearEphemeralKey(GetInstancePtr());
+ }
+ /**
+ * @cli ba ephemeralkey callback (enable, disable)
+ * @code
+ * ba ephemeralkey callback enable
+ * Done
+ * ba ephemeralkey set W10X1 5000 49155
+ * Done
+ * BorderAgent callback: Ephemeral key active, port:49155
+ * BorderAgent callback: Ephemeral key inactive
+ * @endcode
+ * @par api_copy
+ * #otBorderAgentSetEphemeralKeyCallback
+ */
+ else if (aArgs[1] == "callback")
+ {
+ bool enable;
+
+ SuccessOrExit(error = ParseEnableOrDisable(aArgs[2], enable));
+
+ if (enable)
+ {
+ otBorderAgentSetEphemeralKeyCallback(GetInstancePtr(), HandleBorderAgentEphemeralKeyStateChange, this);
+ }
+ else
+ {
+ otBorderAgentSetEphemeralKeyCallback(GetInstancePtr(), nullptr, nullptr);
+ }
+ }
+ else
+ {
+ error = OT_ERROR_INVALID_ARGS;
+ }
+ }
+#endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
else
{
ExitNow(error = OT_ERROR_INVALID_COMMAND);
@@ -618,6 +692,28 @@
exit:
return error;
}
+
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+void Interpreter::HandleBorderAgentEphemeralKeyStateChange(void *aContext)
+{
+ reinterpret_cast<Interpreter *>(aContext)->HandleBorderAgentEphemeralKeyStateChange();
+}
+
+void Interpreter::HandleBorderAgentEphemeralKeyStateChange(void)
+{
+ bool active = otBorderAgentIsEphemeralKeyActive(GetInstancePtr());
+
+ OutputFormat("BorderAgent callback: Ephemeral key %sactive", active ? "" : "in");
+
+ if (active)
+ {
+ OutputFormat(", port:%u", otBorderAgentGetUdpPort(GetInstancePtr()));
+ }
+
+ OutputNewLine();
+}
+#endif
+
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
@@ -1330,7 +1426,9 @@
}
}
#endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+ (OPENTHREAD_FTD || \
+ (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
else if (aArgs[0] == "manager")
{
/**
@@ -1347,26 +1445,42 @@
* @endcode
* @par
* Get the channel manager state.
- * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` is required.
+ * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE` is required.
* @sa otChannelManagerGetRequestedChannel
*/
if (aArgs[1].IsEmpty())
{
OutputLine("channel: %u", otChannelManagerGetRequestedChannel(GetInstancePtr()));
+#if OPENTHREAD_FTD
OutputLine("auto: %d", otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()));
+#endif
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+ OutputLine("autocsl: %u", otChannelManagerGetAutoCslChannelSelectionEnabled(GetInstancePtr()));
+#endif
+#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && \
+ OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+ if (otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()) ||
+ otChannelManagerGetAutoCslChannelSelectionEnabled(GetInstancePtr()))
+#elif OPENTHREAD_FTD
if (otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr()))
+#elif (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+ if (otChannelManagerGetAutoCslChannelSelectionEnabled(GetInstancePtr()))
+#endif
{
Mac::ChannelMask supportedMask(otChannelManagerGetSupportedChannels(GetInstancePtr()));
Mac::ChannelMask favoredMask(otChannelManagerGetFavoredChannels(GetInstancePtr()));
-
+#if OPENTHREAD_FTD
OutputLine("delay: %u", otChannelManagerGetDelay(GetInstancePtr()));
+#endif
OutputLine("interval: %lu", ToUlong(otChannelManagerGetAutoChannelSelectionInterval(GetInstancePtr())));
OutputLine("cca threshold: 0x%04x", otChannelManagerGetCcaFailureRateThreshold(GetInstancePtr()));
OutputLine("supported: %s", supportedMask.ToString().AsCString());
OutputLine("favored: %s", favoredMask.ToString().AsCString());
}
}
+#if OPENTHREAD_FTD
/**
* @cli channel manager change
* @code
@@ -1395,7 +1509,9 @@
* @cparam channel manager select @ca{skip-quality-check}
* Use a `1` or `0` for the boolean `skip-quality-check`.
* @par
- * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
+ * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
+ * are required.
* @par api_copy
* #otChannelManagerRequestChannelSelect
*/
@@ -1406,7 +1522,7 @@
SuccessOrExit(error = aArgs[2].ParseAsBool(enable));
error = otChannelManagerRequestChannelSelect(GetInstancePtr(), enable);
}
-#endif
+#endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
/**
* @cli channel manager auto
* @code
@@ -1417,7 +1533,9 @@
* @cparam channel manager auto @ca{enable}
* `1` is a boolean to `enable`.
* @par
- * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
+ * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
+ * are required.
* @par api_copy
* #otChannelManagerSetAutoChannelSelectionEnabled
*/
@@ -1428,6 +1546,32 @@
SuccessOrExit(error = aArgs[2].ParseAsBool(enable));
otChannelManagerSetAutoChannelSelectionEnabled(GetInstancePtr(), enable);
}
+#endif // OPENTHREAD_FTD
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+ /**
+ * @cli channel manager autocsl
+ * @code
+ * channel manager autocsl 1
+ * Done
+ * @endcode
+ * @cparam channel manager autocsl @ca{enable}
+ * `1` is a boolean to `enable`.
+ * @par
+ * Enables or disables the auto channel selection functionality for a CSL channel.
+ * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
+ * are required.
+ * @sa otChannelManagerSetAutoCslChannelSelectionEnabled
+ */
+ else if (aArgs[1] == "autocsl")
+ {
+ bool enable;
+
+ SuccessOrExit(error = aArgs[2].ParseAsBool(enable));
+ otChannelManagerSetAutoCslChannelSelectionEnabled(GetInstancePtr(), enable);
+ }
+#endif // (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+#if OPENTHREAD_FTD
/**
* @cli channel manager delay
* @code
@@ -1445,6 +1589,7 @@
{
error = ProcessGetSet(aArgs + 2, otChannelManagerGetDelay, otChannelManagerSetDelay);
}
+#endif
/**
* @cli channel manager interval
* @code
@@ -1454,7 +1599,9 @@
* @endcode
* @cparam channel manager interval @ca{interval-seconds}
* @par
- * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
+ * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
+ * are required.
* @par api_copy
* #otChannelManagerSetAutoChannelSelectionInterval
*/
@@ -1471,7 +1618,9 @@
* @endcode
* @cparam channel manager supported @ca{mask}
* @par
- * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
+ * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
+ * are required.
* @par api_copy
* #otChannelManagerSetSupportedChannels
*/
@@ -1488,7 +1637,9 @@
* @endcode
* @cparam channel manager favored @ca{mask}
* @par
- * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
+ * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
+ * are required.
* @par api_copy
* #otChannelManagerSetFavoredChannels
*/
@@ -1506,7 +1657,9 @@
* @cparam channel manager threshold @ca{threshold-percent}
* Use a hex value for `threshold-percent`. `0` maps to 0% and `0xffff` maps to 100%.
* @par
- * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required.
+ * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` or `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE &&
+ * OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE`, and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE`
+ * are required.
* @par api_copy
* #otChannelManagerSetCcaFailureRateThreshold
*/
@@ -2371,9 +2524,9 @@
*/
if (aArgs[0].IsEmpty())
{
- OutputLine("Channel: %u", otLinkGetCslChannel(GetInstancePtr()));
- OutputLine("Period: %luus", ToUlong(otLinkGetCslPeriod(GetInstancePtr())));
- OutputLine("Timeout: %lus", ToUlong(otLinkGetCslTimeout(GetInstancePtr())));
+ OutputLine("channel: %u", otLinkGetCslChannel(GetInstancePtr()));
+ OutputLine("period: %luus", ToUlong(otLinkGetCslPeriod(GetInstancePtr())));
+ OutputLine("timeout: %lus", ToUlong(otLinkGetCslTimeout(GetInstancePtr())));
}
/**
* @cli csl channel
@@ -8211,76 +8364,6 @@
Interpreter::sInterpreter = new (&sInterpreterRaw) Interpreter(instance, aCallback, aContext);
}
-otError Interpreter::ProcessEnableDisable(Arg aArgs[], SetEnabledHandler aSetEnabledHandler)
-{
- otError error = OT_ERROR_NONE;
- bool enable;
-
- if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
- {
- aSetEnabledHandler(GetInstancePtr(), enable);
- }
- else
- {
- error = OT_ERROR_INVALID_COMMAND;
- }
-
- return error;
-}
-
-otError Interpreter::ProcessEnableDisable(Arg aArgs[], SetEnabledHandlerFailable aSetEnabledHandler)
-{
- otError error = OT_ERROR_NONE;
- bool enable;
-
- if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
- {
- error = aSetEnabledHandler(GetInstancePtr(), enable);
- }
- else
- {
- error = OT_ERROR_INVALID_COMMAND;
- }
-
- return error;
-}
-
-otError Interpreter::ProcessEnableDisable(Arg aArgs[],
- IsEnabledHandler aIsEnabledHandler,
- SetEnabledHandler aSetEnabledHandler)
-{
- otError error = OT_ERROR_NONE;
-
- if (aArgs[0].IsEmpty())
- {
- OutputEnabledDisabledStatus(aIsEnabledHandler(GetInstancePtr()));
- }
- else
- {
- error = ProcessEnableDisable(aArgs, aSetEnabledHandler);
- }
-
- return error;
-}
-
-otError Interpreter::ProcessEnableDisable(Arg aArgs[],
- IsEnabledHandler aIsEnabledHandler,
- SetEnabledHandlerFailable aSetEnabledHandler)
-{
- otError error = OT_ERROR_NONE;
-
- if (aArgs[0].IsEmpty())
- {
- OutputEnabledDisabledStatus(aIsEnabledHandler(GetInstancePtr()));
- }
- else
- {
- error = ProcessEnableDisable(aArgs, aSetEnabledHandler);
- }
-
- return error;
-}
-
void Interpreter::OutputPrompt(void)
{
#if OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE
diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp
index 142f02c..75ea629 100644
--- a/src/cli/cli.hpp
+++ b/src/cli/cli.hpp
@@ -68,13 +68,13 @@
#include "cli/cli_link_metrics.hpp"
#include "cli/cli_mac_filter.hpp"
#include "cli/cli_network_data.hpp"
-#include "cli/cli_output.hpp"
#include "cli/cli_ping.hpp"
#include "cli/cli_srp_client.hpp"
#include "cli/cli_srp_server.hpp"
#include "cli/cli_tcat.hpp"
#include "cli/cli_tcp.hpp"
#include "cli/cli_udp.hpp"
+#include "cli/cli_utils.hpp"
#if OPENTHREAD_CONFIG_COAP_API_ENABLE
#include "cli/cli_coap.hpp"
#endif
@@ -108,7 +108,7 @@
* Implements the CLI interpreter.
*
*/
-class Interpreter : public OutputImplementer, public Output
+class Interpreter : public OutputImplementer, public Utils
{
#if OPENTHREAD_FTD || OPENTHREAD_MTD
friend class Br;
@@ -128,8 +128,6 @@
friend void otCliOutputFormat(const char *aFmt, ...);
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
@@ -179,19 +177,6 @@
void ProcessLine(char *aBuf);
/**
- * Checks a given argument string against "enable" or "disable" commands.
- *
- * @param[in] aArg The argument string to parse.
- * @param[out] aEnable Boolean variable to return outcome on success.
- * Set to TRUE for "enable" command, and FALSE for "disable" command.
- *
- * @retval OT_ERROR_NONE Successfully parsed the @p aString and updated @p aEnable.
- * @retval OT_ERROR_INVALID_COMMAND The @p aString is not "enable" or "disable" command.
- *
- */
- static otError ParseEnableOrDisable(const Arg &aArg, bool &aEnable);
-
- /**
* Adds commands to the user command table.
*
* @param[in] aCommands A pointer to an array with user commands.
@@ -289,94 +274,6 @@
using Command = CommandEntry<Interpreter>;
- template <typename ValueType> using GetHandler = ValueType (&)(otInstance *);
- template <typename ValueType> using SetHandler = void (&)(otInstance *, ValueType);
- template <typename ValueType> using SetHandlerFailable = otError (&)(otInstance *, ValueType);
- using IsEnabledHandler = bool (&)(otInstance *);
- using SetEnabledHandler = void (&)(otInstance *, bool);
- using SetEnabledHandlerFailable = otError (&)(otInstance *, bool);
-
- // Returns format string to output a `ValueType` (e.g., "%u" for `uint16_t`).
- template <typename ValueType> static constexpr const char *FormatStringFor(void);
-
- // General template implementation.
- // Specializations for `uint32_t` and `int32_t` are added at the end.
- template <typename ValueType> otError ProcessGet(Arg aArgs[], GetHandler<ValueType> aGetHandler)
- {
- static_assert(
- TypeTraits::IsSame<ValueType, uint8_t>::kValue || TypeTraits::IsSame<ValueType, uint16_t>::kValue ||
- TypeTraits::IsSame<ValueType, int8_t>::kValue || TypeTraits::IsSame<ValueType, int16_t>::kValue ||
- TypeTraits::IsSame<ValueType, const char *>::kValue,
- "ValueType must be an 8, 16 `int` or `uint` type, or a `const char *`");
-
- otError error = OT_ERROR_NONE;
-
- VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
- OutputLine(FormatStringFor<ValueType>(), aGetHandler(GetInstancePtr()));
-
- exit:
- return error;
- }
-
- template <typename ValueType> otError ProcessSet(Arg aArgs[], SetHandler<ValueType> aSetHandler)
- {
- otError error;
- ValueType value;
-
- SuccessOrExit(error = aArgs[0].ParseAs<ValueType>(value));
- VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
-
- aSetHandler(GetInstancePtr(), value);
-
- exit:
- return error;
- }
-
- template <typename ValueType> otError ProcessSet(Arg aArgs[], SetHandlerFailable<ValueType> aSetHandler)
- {
- otError error;
- ValueType value;
-
- SuccessOrExit(error = aArgs[0].ParseAs<ValueType>(value));
- VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
-
- error = aSetHandler(GetInstancePtr(), value);
-
- exit:
- return error;
- }
-
- template <typename ValueType>
- otError ProcessGetSet(Arg aArgs[], GetHandler<ValueType> aGetHandler, SetHandler<ValueType> aSetHandler)
- {
- otError error = ProcessGet(aArgs, aGetHandler);
-
- VerifyOrExit(error != OT_ERROR_NONE);
- error = ProcessSet(aArgs, aSetHandler);
-
- exit:
- return error;
- }
-
- template <typename ValueType>
- otError ProcessGetSet(Arg aArgs[], GetHandler<ValueType> aGetHandler, SetHandlerFailable<ValueType> aSetHandler)
- {
- otError error = ProcessGet(aArgs, aGetHandler);
-
- VerifyOrExit(error != OT_ERROR_NONE);
- error = ProcessSet(aArgs, aSetHandler);
-
- exit:
- return error;
- }
-
- otError ProcessEnableDisable(Arg aArgs[], SetEnabledHandler aSetEnabledHandler);
- otError ProcessEnableDisable(Arg aArgs[], SetEnabledHandlerFailable aSetEnabledHandler);
- otError ProcessEnableDisable(Arg aArgs[], IsEnabledHandler aIsEnabledHandler, SetEnabledHandler aSetEnabledHandler);
- otError ProcessEnableDisable(Arg aArgs[],
- IsEnabledHandler aIsEnabledHandler,
- SetEnabledHandlerFailable aSetEnabledHandler);
-
void OutputPrompt(void);
void OutputResult(otError aError);
@@ -495,6 +392,11 @@
void HandleSntpResponse(uint64_t aTime, otError aResult);
#endif
+#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE && OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+ static void HandleBorderAgentEphemeralKeyStateChange(void *aContext);
+ void HandleBorderAgentEphemeralKeyStateChange(void);
+#endif
+
static void HandleDetachGracefullyResult(void *aContext);
void HandleDetachGracefullyResult(void);
@@ -599,46 +501,6 @@
#endif
};
-// Specializations of `FormatStringFor<ValueType>()`
-
-template <> inline constexpr const char *Interpreter::FormatStringFor<uint8_t>(void) { return "%u"; }
-
-template <> inline constexpr const char *Interpreter::FormatStringFor<uint16_t>(void) { return "%u"; }
-
-template <> inline constexpr const char *Interpreter::FormatStringFor<uint32_t>(void) { return "%lu"; }
-
-template <> inline constexpr const char *Interpreter::FormatStringFor<int8_t>(void) { return "%d"; }
-
-template <> inline constexpr const char *Interpreter::FormatStringFor<int16_t>(void) { return "%d"; }
-
-template <> inline constexpr const char *Interpreter::FormatStringFor<int32_t>(void) { return "%ld"; }
-
-template <> inline constexpr const char *Interpreter::FormatStringFor<const char *>(void) { return "%s"; }
-
-// Specialization of ProcessGet<> for `uint32_t` and `int32_t`
-
-template <> inline otError Interpreter::ProcessGet<uint32_t>(Arg aArgs[], GetHandler<uint32_t> aGetHandler)
-{
- otError error = OT_ERROR_NONE;
-
- VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
- OutputLine(FormatStringFor<uint32_t>(), ToUlong(aGetHandler(GetInstancePtr())));
-
-exit:
- return error;
-}
-
-template <> inline otError Interpreter::ProcessGet<int32_t>(Arg aArgs[], GetHandler<int32_t> aGetHandler)
-{
- otError error = OT_ERROR_NONE;
-
- VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
- OutputLine(FormatStringFor<int32_t>(), static_cast<long int>(aGetHandler(GetInstancePtr())));
-
-exit:
- return error;
-}
-
} // namespace Cli
} // namespace ot
diff --git a/src/cli/cli_bbr.cpp b/src/cli/cli_bbr.cpp
index d37d3df..0f86b24 100644
--- a/src/cli/cli_bbr.cpp
+++ b/src/cli/cli_bbr.cpp
@@ -290,8 +290,7 @@
*/
template <> otError Bbr::Process<Cmd("jitter")>(Arg aArgs[])
{
- return Interpreter::GetInterpreter().ProcessGetSet(aArgs, otBackboneRouterGetRegistrationJitter,
- otBackboneRouterSetRegistrationJitter);
+ return ProcessGetSet(aArgs, otBackboneRouterGetRegistrationJitter, otBackboneRouterSetRegistrationJitter);
}
/**
diff --git a/src/cli/cli_bbr.hpp b/src/cli/cli_bbr.hpp
index 40ed979..120518a 100644
--- a/src/cli/cli_bbr.hpp
+++ b/src/cli/cli_bbr.hpp
@@ -42,7 +42,7 @@
#include <openthread/backbone_router_ftd.h>
#include "cli/cli_config.h"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
namespace ot {
namespace Cli {
@@ -51,11 +51,9 @@
* Implements the BBR CLI interpreter.
*
*/
-class Bbr : private Output
+class Bbr : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor.
*
@@ -64,7 +62,7 @@
*
*/
Bbr(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
{
}
diff --git a/src/cli/cli_br.cpp b/src/cli/cli_br.cpp
index 9c77c7d..53f2b32 100644
--- a/src/cli/cli_br.cpp
+++ b/src/cli/cli_br.cpp
@@ -455,7 +455,7 @@
* #otBorderRoutingDhcp6PdSetEnabled
*
*/
- if (Interpreter::GetInterpreter().ProcessEnableDisable(aArgs, otBorderRoutingDhcp6PdSetEnabled) == OT_ERROR_NONE)
+ if (ProcessEnableDisable(aArgs, otBorderRoutingDhcp6PdSetEnabled) == OT_ERROR_NONE)
{
}
/**
@@ -538,6 +538,46 @@
aEntry.mStubRouterFlag);
}
+template <> otError Br::Process<Cmd("raoptions")>(Arg aArgs[])
+{
+ static constexpr uint16_t kMaxExtraOptions = 800;
+
+ otError error = OT_ERROR_NONE;
+ uint8_t options[kMaxExtraOptions];
+ uint16_t length;
+
+ /**
+ * @cli br raoptions (set,clear)
+ * @code
+ * br raoptions 0400ff00020001
+ * Done
+ * @endcode
+ * @code
+ * br raoptions clear
+ * Done
+ * @endcode
+ * @cparam br raoptions @ca{options|clear}
+ * `br raoptions clear` passes a `nullptr` to #otBorderRoutingSetExtraRouterAdvertOptions.
+ * Otherwise, you can pass the `options` byte as hex data.
+ * @par api_copy
+ * #otBorderRoutingSetExtraRouterAdvertOptions
+ */
+ if (aArgs[0] == "clear")
+ {
+ length = 0;
+ }
+ else
+ {
+ length = sizeof(options);
+ SuccessOrExit(error = aArgs[0].ParseAsHexString(length, options));
+ }
+
+ error = otBorderRoutingSetExtraRouterAdvertOptions(GetInstancePtr(), length > 0 ? options : nullptr, length);
+
+exit:
+ return error;
+}
+
template <> otError Br::Process<Cmd("rioprf")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
@@ -702,6 +742,7 @@
CmdEntry("pd"),
#endif
CmdEntry("prefixtable"),
+ CmdEntry("raoptions"),
CmdEntry("rioprf"),
CmdEntry("routeprf"),
CmdEntry("routers"),
diff --git a/src/cli/cli_br.hpp b/src/cli/cli_br.hpp
index 01fcdcf..5a877a8 100644
--- a/src/cli/cli_br.hpp
+++ b/src/cli/cli_br.hpp
@@ -39,7 +39,7 @@
#include <openthread/border_routing.h>
#include "cli/cli_config.h"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
@@ -50,11 +50,9 @@
* Implements the Border Router CLI interpreter.
*
*/
-class Br : private Output
+class Br : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
@@ -63,7 +61,7 @@
*
*/
Br(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
{
}
diff --git a/src/cli/cli_coap.cpp b/src/cli/cli_coap.cpp
index 9464dff..5edbe00 100644
--- a/src/cli/cli_coap.cpp
+++ b/src/cli/cli_coap.cpp
@@ -45,7 +45,7 @@
namespace Cli {
Coap::Coap(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
, mUseDefaultRequestTxParameters(true)
, mUseDefaultResponseTxParameters(true)
#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
@@ -949,7 +949,7 @@
}
else
{
- responseCode = OT_COAP_CODE_VALID;
+ responseCode = OT_COAP_CODE_CHANGED;
}
responseMessage = otCoapNewMessage(GetInstancePtr(), nullptr);
diff --git a/src/cli/cli_coap.hpp b/src/cli/cli_coap.hpp
index 2b4184a..95ce7b9 100644
--- a/src/cli/cli_coap.hpp
+++ b/src/cli/cli_coap.hpp
@@ -40,7 +40,7 @@
#include <openthread/coap.h>
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
namespace ot {
namespace Cli {
@@ -49,11 +49,9 @@
* Implements the CLI CoAP server and client.
*
*/
-class Coap : private Output
+class Coap : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
diff --git a/src/cli/cli_coap_secure.cpp b/src/cli/cli_coap_secure.cpp
index c1bacdb..2ff4f68 100644
--- a/src/cli/cli_coap_secure.cpp
+++ b/src/cli/cli_coap_secure.cpp
@@ -47,7 +47,7 @@
namespace Cli {
CoapSecure::CoapSecure(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
, mShutdownFlag(false)
, mUseCertificate(false)
, mPskLength(0)
@@ -103,7 +103,7 @@
* @endcode
* @cparam coaps resource [@ca{uri-path}]
* @par
- * Gets or sets the URI path of the CoAPS server resource.
+ * Gets or sets the URI path of the CoAPS server resource. @moreinfo{@coaps}.
* @sa otCoapSecureAddBlockWiseResource
*/
template <> otError CoapSecure::Process<Cmd("resource")>(Arg aArgs[])
@@ -152,7 +152,7 @@
* @endcode
* @cparam coaps set @ca{new-content}
* @par
- * Sets the content sent by the resource on the CoAPS server.
+ * Sets the content sent by the resource on the CoAPS server. @moreinfo{@coaps}.
*/
template <> otError CoapSecure::Process<Cmd("set")>(Arg aArgs[])
{
@@ -207,7 +207,7 @@
* `check-peer-cert` is `true`, and the `max-conn-attempts` value is the
* number specified in the argument.
* @par
- * Starts the CoAP Secure service.
+ * Starts the CoAP Secure service. @moreinfo{@coaps}.
* @sa otCoapSecureStart
* @sa otCoapSecureSetSslAuthMode
* @sa otCoapSecureSetClientConnectedCallback
@@ -255,7 +255,7 @@
* Done
* @endcode
* @par
- * Stops the CoAP Secure service.
+ * Stops the CoAP Secure service. @moreinfo{@coaps}.
* @sa otCoapSecureStop
*/
template <> otError CoapSecure::Process<Cmd("stop")>(Arg aArgs[])
@@ -289,7 +289,7 @@
* Done
* @endcode
* @par
- * Indicates if the CoAP Secure service is closed.
+ * Indicates if the CoAP Secure service is closed. @moreinfo{@coaps}.
* @sa otCoapSecureIsClosed
*/
template <> otError CoapSecure::Process<Cmd("isclosed")>(Arg aArgs[])
@@ -305,7 +305,7 @@
* Done
* @endcode
* @par
- * Indicates if the CoAP Secure service is connected.
+ * Indicates if the CoAP Secure service is connected. @moreinfo{@coaps}.
* @sa otCoapSecureIsConnected
*/
template <> otError CoapSecure::Process<Cmd("isconnected")>(Arg aArgs[])
@@ -323,6 +323,7 @@
* @par
* Indicates if the CoAP Secure service connection is active
* (either already connected or in the process of establishing a connection).
+ * @moreinfo{@coaps}.
* @sa otCoapSecureIsConnectionActive
*/
template <> otError CoapSecure::Process<Cmd("isconnactive")>(Arg aArgs[])
@@ -362,6 +363,7 @@
* `block-256`, `block-512`, or `block-1024`.
* @par
* Gets information about the specified CoAPS resource on the CoAPS server.
+ * @moreinfo{@coaps}.
*/
template <> otError CoapSecure::Process<Cmd("get")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_GET); }
@@ -393,7 +395,7 @@
* integer that specifies the number of blocks to send. The `block-` type
* requires `OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE` to be set.
* @par
- * Creates the specified CoAPS resource.
+ * Creates the specified CoAPS resource. @moreinfo{@coaps}.
*/
template <> otError CoapSecure::Process<Cmd("post")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_POST); }
@@ -425,7 +427,7 @@
* integer that specifies the number of blocks to send. The `block-` type
* requires `OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE` to be set.
* @par
- * Modifies the specified CoAPS resource.
+ * Modifies the specified CoAPS resource. @moreinfo{@coaps}.
*/
template <> otError CoapSecure::Process<Cmd("put")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_PUT); }
@@ -610,6 +612,7 @@
* The `address` parameter is the IPv6 address of the peer.
* @par
* Initializes a Datagram Transport Layer Security (DTLS) session with a peer.
+ * @moreinfo{@coaps}.
* @sa otCoapSecureConnect
*/
template <> otError CoapSecure::Process<Cmd("connect")>(Arg aArgs[])
diff --git a/src/cli/cli_coap_secure.hpp b/src/cli/cli_coap_secure.hpp
index a09dfed..92a7a95 100644
--- a/src/cli/cli_coap_secure.hpp
+++ b/src/cli/cli_coap_secure.hpp
@@ -42,7 +42,7 @@
#include <openthread/coap_secure.h>
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
#ifndef CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER
#define CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER 0
@@ -55,11 +55,9 @@
* Implements the CLI CoAP Secure server and client.
*
*/
-class CoapSecure : private Output
+class CoapSecure : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
diff --git a/src/cli/cli_commissioner.hpp b/src/cli/cli_commissioner.hpp
index 162c6da..a5feac4 100644
--- a/src/cli/cli_commissioner.hpp
+++ b/src/cli/cli_commissioner.hpp
@@ -38,7 +38,7 @@
#include <openthread/commissioner.h>
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
@@ -49,11 +49,9 @@
* Implements the Commissioner CLI interpreter.
*
*/
-class Commissioner : private Output
+class Commissioner : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
@@ -62,7 +60,7 @@
*
*/
Commissioner(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
{
}
diff --git a/src/cli/cli_dataset.cpp b/src/cli/cli_dataset.cpp
index cfd47a1..63e3203 100644
--- a/src/cli/cli_dataset.cpp
+++ b/src/cli/cli_dataset.cpp
@@ -1154,10 +1154,46 @@
{
otError error = OT_ERROR_NONE;
+ /**
+ * @cli dataset updater
+ * @code
+ * dataset updater
+ * Enabled
+ * Done
+ * @endcode
+ * @par api_copy
+ * #otDatasetUpdaterIsUpdateOngoing
+ */
if (aArgs[0].IsEmpty())
{
OutputEnabledDisabledStatus(otDatasetUpdaterIsUpdateOngoing(GetInstancePtr()));
}
+ /**
+ * @cli dataset updater start
+ * @code
+ * channel
+ * 19
+ * Done
+ * dataset clear
+ * Done
+ * dataset channel 15
+ * Done
+ * dataset
+ * Channel: 15
+ * Done
+ * dataset updater start
+ * Done
+ * dataset updater
+ * Enabled
+ * Done
+ * Dataset update complete: OK
+ * channel
+ * 15
+ * Done
+ * @endcode
+ * @par api_copy
+ * #otDatasetUpdaterRequestUpdate
+ */
else if (aArgs[0] == "start")
{
otOperationalDataset dataset;
@@ -1166,6 +1202,15 @@
SuccessOrExit(
error = otDatasetUpdaterRequestUpdate(GetInstancePtr(), &dataset, &Dataset::HandleDatasetUpdater, this));
}
+ /**
+ * @cli dataset updater cancel
+ * @code
+ * @dataset updater cancel
+ * Done
+ * @endcode
+ * @par api_copy
+ * #otDatasetUpdaterCancelUpdate
+ */
else if (aArgs[0] == "cancel")
{
otDatasetUpdaterCancelUpdate(GetInstancePtr());
diff --git a/src/cli/cli_dataset.hpp b/src/cli/cli_dataset.hpp
index 8587862..06055e3 100644
--- a/src/cli/cli_dataset.hpp
+++ b/src/cli/cli_dataset.hpp
@@ -40,7 +40,7 @@
#include <openthread/dataset.h>
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
namespace ot {
namespace Cli {
@@ -49,13 +49,11 @@
* Implements the Dataset CLI interpreter.
*
*/
-class Dataset : private Output
+class Dataset : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
Dataset(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
{
}
diff --git a/src/cli/cli_dns.cpp b/src/cli/cli_dns.cpp
index e5dff9a..5148bb3 100644
--- a/src/cli/cli_dns.cpp
+++ b/src/cli/cli_dns.cpp
@@ -679,8 +679,7 @@
* @par api_copy
* #otDnssdUpstreamQuerySetEnabled
*/
- error = Interpreter::GetInterpreter().ProcessEnableDisable(aArgs + 1, otDnssdUpstreamQueryIsEnabled,
- otDnssdUpstreamQuerySetEnabled);
+ error = ProcessEnableDisable(aArgs + 1, otDnssdUpstreamQueryIsEnabled, otDnssdUpstreamQuerySetEnabled);
}
#endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
else
diff --git a/src/cli/cli_dns.hpp b/src/cli/cli_dns.hpp
index b9f1679..bf31ed7 100644
--- a/src/cli/cli_dns.hpp
+++ b/src/cli/cli_dns.hpp
@@ -54,7 +54,7 @@
#include <openthread/dnssd_server.h>
#include "cli/cli_config.h"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
namespace ot {
namespace Cli {
@@ -63,11 +63,9 @@
* Implements the DNS CLI interpreter.
*
*/
-class Dns : private Output
+class Dns : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor.
*
@@ -76,7 +74,7 @@
*
*/
Dns(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
{
}
diff --git a/src/cli/cli_history.hpp b/src/cli/cli_history.hpp
index 90f832f..6958b04 100644
--- a/src/cli/cli_history.hpp
+++ b/src/cli/cli_history.hpp
@@ -39,7 +39,7 @@
#include <openthread/history_tracker.h>
#include "cli/cli_config.h"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
@@ -50,11 +50,9 @@
* Implements the History Tracker CLI interpreter.
*
*/
-class History : private Output
+class History : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
@@ -63,7 +61,7 @@
*
*/
History(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
{
}
diff --git a/src/cli/cli_joiner.hpp b/src/cli/cli_joiner.hpp
index e45dbf8..297525c 100644
--- a/src/cli/cli_joiner.hpp
+++ b/src/cli/cli_joiner.hpp
@@ -38,7 +38,7 @@
#include <openthread/joiner.h>
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
#if OPENTHREAD_CONFIG_JOINER_ENABLE
@@ -49,11 +49,9 @@
* Implements the Joiner CLI interpreter.
*
*/
-class Joiner : private Output
+class Joiner : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
@@ -62,7 +60,7 @@
*
*/
Joiner(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
{
}
diff --git a/src/cli/cli_link_metrics.cpp b/src/cli/cli_link_metrics.cpp
index 93cc8c5..a12515d 100644
--- a/src/cli/cli_link_metrics.cpp
+++ b/src/cli/cli_link_metrics.cpp
@@ -36,7 +36,7 @@
#include <openthread/link_metrics.h>
#include "cli/cli.hpp"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
#include "common/code_utils.hpp"
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
@@ -45,7 +45,7 @@
namespace Cli {
LinkMetrics::LinkMetrics(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
, mLinkMetricsQueryInProgress(false)
{
}
diff --git a/src/cli/cli_link_metrics.hpp b/src/cli/cli_link_metrics.hpp
index d62be78..40b67c6 100644
--- a/src/cli/cli_link_metrics.hpp
+++ b/src/cli/cli_link_metrics.hpp
@@ -38,7 +38,7 @@
#include <openthread/link_metrics.h>
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
@@ -50,11 +50,9 @@
*
*/
-class LinkMetrics : private Output
+class LinkMetrics : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
diff --git a/src/cli/cli_mac_filter.hpp b/src/cli/cli_mac_filter.hpp
index 1394d4f..0f03b1b 100644
--- a/src/cli/cli_mac_filter.hpp
+++ b/src/cli/cli_mac_filter.hpp
@@ -41,7 +41,7 @@
#include <openthread/link.h>
#include "cli/cli_config.h"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
namespace ot {
namespace Cli {
@@ -50,11 +50,9 @@
* Implements the MAC Filter CLI interpreter.
*
*/
-class MacFilter : private Output
+class MacFilter : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor.
*
@@ -63,7 +61,7 @@
*
*/
MacFilter(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
{
}
diff --git a/src/cli/cli_network_data.cpp b/src/cli/cli_network_data.cpp
index 85c3e26..7aa0dd5 100644
--- a/src/cli/cli_network_data.cpp
+++ b/src/cli/cli_network_data.cpp
@@ -44,7 +44,7 @@
namespace Cli {
NetworkData::NetworkData(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
{
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
mFullCallbackWasCalled = false;
@@ -564,17 +564,78 @@
return error;
}
-void NetworkData::OutputPrefixes(bool aLocal)
+void NetworkData::OutputNetworkData(bool aLocal, uint16_t aRloc16)
{
- otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
- otBorderRouterConfig config;
+ otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
+ otBorderRouterConfig prefix;
+ otExternalRouteConfig route;
+ otServiceConfig service;
+ otLowpanContextInfo context;
+ otCommissioningDataset dataset;
OutputLine("Prefixes:");
- while (GetNextPrefix(&iterator, &config, aLocal) == OT_ERROR_NONE)
+ while (GetNextPrefix(&iterator, &prefix, aLocal) == OT_ERROR_NONE)
{
- OutputPrefix(config);
+ if ((aRloc16 == kAnyRloc16) || (aRloc16 == prefix.mRloc16))
+ {
+ OutputPrefix(prefix);
+ }
}
+
+ OutputLine("Routes:");
+ iterator = OT_NETWORK_DATA_ITERATOR_INIT;
+
+ while (GetNextRoute(&iterator, &route, aLocal) == OT_ERROR_NONE)
+ {
+ if ((aRloc16 == kAnyRloc16) || (aRloc16 == route.mRloc16))
+ {
+ OutputRoute(route);
+ }
+ }
+
+ OutputLine("Services:");
+ iterator = OT_NETWORK_DATA_ITERATOR_INIT;
+
+ while (GetNextService(&iterator, &service, aLocal) == OT_ERROR_NONE)
+ {
+ if ((aRloc16 == kAnyRloc16) || (aRloc16 == service.mServerConfig.mRloc16))
+ {
+ OutputService(service);
+ }
+ }
+
+ VerifyOrExit(!aLocal);
+ VerifyOrExit(aRloc16 == kAnyRloc16);
+
+ OutputLine("Contexts:");
+ iterator = OT_NETWORK_DATA_ITERATOR_INIT;
+
+ while (otNetDataGetNextLowpanContextInfo(GetInstancePtr(), &iterator, &context) == OT_ERROR_NONE)
+ {
+ OutputIp6Prefix(context.mPrefix);
+ OutputLine(" %u %c", context.mContextId, context.mCompressFlag ? 'c' : '-');
+ }
+
+ otNetDataGetCommissioningDataset(GetInstancePtr(), &dataset);
+
+ OutputLine("Commissioning:");
+
+ dataset.mIsSessionIdSet ? OutputFormat("%u ", dataset.mSessionId) : OutputFormat("- ");
+ dataset.mIsLocatorSet ? OutputFormat("%04x ", dataset.mLocator) : OutputFormat("- ");
+ dataset.mIsJoinerUdpPortSet ? OutputFormat("%u ", dataset.mJoinerUdpPort) : OutputFormat("- ");
+ dataset.mIsSteeringDataSet ? OutputBytes(dataset.mSteeringData.m8, dataset.mSteeringData.mLength)
+ : OutputFormat("-");
+
+ if (dataset.mHasExtraTlv)
+ {
+ OutputFormat(" e");
+ }
+
+ OutputNewLine();
+
+exit:
+ return;
}
otError NetworkData::GetNextRoute(otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig, bool aLocal)
@@ -597,19 +658,6 @@
return error;
}
-void NetworkData::OutputRoutes(bool aLocal)
-{
- otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
- otExternalRouteConfig config;
-
- OutputLine("Routes:");
-
- while (GetNextRoute(&iterator, &config, aLocal) == OT_ERROR_NONE)
- {
- OutputRoute(config);
- }
-}
-
otError NetworkData::GetNextService(otNetworkDataIterator *aIterator, otServiceConfig *aConfig, bool aLocal)
{
otError error;
@@ -630,65 +678,6 @@
return error;
}
-void NetworkData::OutputServices(bool aLocal)
-{
- otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
- otServiceConfig config;
-
- OutputLine("Services:");
-
- while (GetNextService(&iterator, &config, aLocal) == OT_ERROR_NONE)
- {
- OutputService(config);
- }
-}
-
-void NetworkData::OutputLowpanContexts(bool aLocal)
-{
- otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
- otLowpanContextInfo info;
-
- VerifyOrExit(!aLocal);
-
- OutputLine("Contexts:");
-
- while (otNetDataGetNextLowpanContextInfo(GetInstancePtr(), &iterator, &info) == OT_ERROR_NONE)
- {
- OutputIp6Prefix(info.mPrefix);
- OutputLine(" %u %c", info.mContextId, info.mCompressFlag ? 'c' : '-');
- }
-
-exit:
- return;
-}
-
-void NetworkData::OutputCommissioningDataset(bool aLocal)
-{
- otCommissioningDataset dataset;
-
- VerifyOrExit(!aLocal);
-
- otNetDataGetCommissioningDataset(GetInstancePtr(), &dataset);
-
- OutputLine("Commissioning:");
-
- dataset.mIsSessionIdSet ? OutputFormat("%u ", dataset.mSessionId) : OutputFormat("- ");
- dataset.mIsLocatorSet ? OutputFormat("%04x ", dataset.mLocator) : OutputFormat("- ");
- dataset.mIsJoinerUdpPortSet ? OutputFormat("%u ", dataset.mJoinerUdpPort) : OutputFormat("- ");
- dataset.mIsSteeringDataSet ? OutputBytes(dataset.mSteeringData.m8, dataset.mSteeringData.mLength)
- : OutputFormat("-");
-
- if (dataset.mHasExtraTlv)
- {
- OutputFormat(" e");
- }
-
- OutputNewLine();
-
-exit:
- return;
-}
-
otError NetworkData::OutputBinary(bool aLocal)
{
otError error;
@@ -737,8 +726,16 @@
* 08040b02174703140040fd00deadbeefcafe0504dc00330007021140
* Done
* @endcode
- * @cparam netdata show [@ca{-x}]
+ * @code
+ * netdata show 0xdc00
+ * Prefixes:
+ * fd00:dead:beef:cafe::/64 paros med dc00
+ * Routes:
+ * Services:
+ * Done
+ * @cparam netdata show [@ca{-x}|@ca{rloc16}]
* * The optional `-x` argument gets Network Data as hex-encoded TLVs.
+ * * The optional `rloc16` argument gets all prefix/route/service entries associated with a given RLOC16.
* @par
* `netdata show` from OT CLI gets full Network Data received from the Leader. This command uses several
* API functions to combine prefixes, routes, and services, including #otNetDataGetNextOnMeshPrefix,
@@ -795,9 +792,10 @@
*/
template <> otError NetworkData::Process<Cmd("show")>(Arg aArgs[])
{
- otError error = OT_ERROR_INVALID_ARGS;
- bool local = false;
- bool binary = false;
+ otError error = OT_ERROR_INVALID_ARGS;
+ uint16_t rloc16 = kAnyRloc16;
+ bool local = false;
+ bool binary = false;
for (uint8_t i = 0; !aArgs[i].IsEmpty(); i++)
{
@@ -832,21 +830,22 @@
}
else
{
- ExitNow(error = OT_ERROR_INVALID_ARGS);
+ SuccessOrExit(error = aArgs[i].ParseAsUint16(rloc16));
}
}
+ if (local || binary)
+ {
+ VerifyOrExit(rloc16 == kAnyRloc16, error = OT_ERROR_INVALID_ARGS);
+ }
+
if (binary)
{
error = OutputBinary(local);
}
else
{
- OutputPrefixes(local);
- OutputRoutes(local);
- OutputServices(local);
- OutputLowpanContexts(local);
- OutputCommissioningDataset(local);
+ OutputNetworkData(local, rloc16);
error = OT_ERROR_NONE;
}
diff --git a/src/cli/cli_network_data.hpp b/src/cli/cli_network_data.hpp
index 15af650..ba8f281 100644
--- a/src/cli/cli_network_data.hpp
+++ b/src/cli/cli_network_data.hpp
@@ -38,7 +38,7 @@
#include <openthread/netdata.h>
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
namespace ot {
namespace Cli {
@@ -47,11 +47,9 @@
* Implements the Network Data CLI.
*
*/
-class NetworkData : private Output
+class NetworkData : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* This constant specifies the string size for representing Network Data prefix/route entry flags.
*
@@ -132,6 +130,8 @@
private:
using Command = CommandEntry<NetworkData>;
+ static constexpr uint16_t kAnyRloc16 = 0xffff;
+
template <CommandId kCommandId> otError Process(Arg aArgs[]);
otError GetNextPrefix(otNetworkDataIterator *aIterator, otBorderRouterConfig *aConfig, bool aLocal);
@@ -139,11 +139,7 @@
otError GetNextService(otNetworkDataIterator *aIterator, otServiceConfig *aConfig, bool aLocal);
otError OutputBinary(bool aLocal);
- void OutputPrefixes(bool aLocal);
- void OutputRoutes(bool aLocal);
- void OutputServices(bool aLocal);
- void OutputLowpanContexts(bool aLocal);
- void OutputCommissioningDataset(bool aLocal);
+ void OutputNetworkData(bool aLocal, uint16_t aRloc16);
#if OPENTHREAD_CONFIG_BORDER_ROUTER_SIGNAL_NETWORK_DATA_FULL
static void HandleNetdataFull(void *aContext) { static_cast<NetworkData *>(aContext)->HandleNetdataFull(); }
diff --git a/src/cli/cli_ping.cpp b/src/cli/cli_ping.cpp
index 7d88259..1111e3d 100644
--- a/src/cli/cli_ping.cpp
+++ b/src/cli/cli_ping.cpp
@@ -36,7 +36,7 @@
#include <openthread/ping_sender.h>
#include "cli/cli.hpp"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
#include "common/code_utils.hpp"
#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
@@ -45,7 +45,7 @@
namespace Cli {
PingSender::PingSender(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
, mPingIsAsync(false)
{
}
diff --git a/src/cli/cli_ping.hpp b/src/cli/cli_ping.hpp
index e517506..6504412 100644
--- a/src/cli/cli_ping.hpp
+++ b/src/cli/cli_ping.hpp
@@ -38,7 +38,7 @@
#include <openthread/ping_sender.h>
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
@@ -50,11 +50,9 @@
*
*/
-class PingSender : private Output
+class PingSender : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
diff --git a/src/cli/cli_srp_client.cpp b/src/cli/cli_srp_client.cpp
index d90947b..a5301f3 100644
--- a/src/cli/cli_srp_client.cpp
+++ b/src/cli/cli_srp_client.cpp
@@ -58,7 +58,7 @@
}
SrpClient::SrpClient(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
, mCallbackEnabled(false)
{
otSrpClientSetCallback(GetInstancePtr(), SrpClient::HandleCallback, this);
@@ -453,7 +453,7 @@
*/
template <> otError SrpClient::Process<Cmd("leaseinterval")>(Arg aArgs[])
{
- return Interpreter::GetInterpreter().ProcessGetSet(aArgs, otSrpClientGetLeaseInterval, otSrpClientSetLeaseInterval);
+ return ProcessGetSet(aArgs, otSrpClientGetLeaseInterval, otSrpClientSetLeaseInterval);
}
/**
@@ -475,8 +475,7 @@
*/
template <> otError SrpClient::Process<Cmd("keyleaseinterval")>(Arg aArgs[])
{
- return Interpreter::GetInterpreter().ProcessGetSet(aArgs, otSrpClientGetKeyLeaseInterval,
- otSrpClientSetKeyLeaseInterval);
+ return ProcessGetSet(aArgs, otSrpClientGetKeyLeaseInterval, otSrpClientSetKeyLeaseInterval);
}
template <> otError SrpClient::Process<Cmd("server")>(Arg aArgs[])
@@ -577,9 +576,9 @@
* * --> [@ca{weight}] [@ca{txt}]
* The `servicename` parameter can optionally include a list of service subtype labels that are
* separated by commas. The examples here use generic naming. The `priority` and `weight` (both are `uint16_t`
- * values) parameters are optional, and if not provided zero is used. The optional `txt` parameter sets the TXT data
- * associated with the service. The `txt` value must be in hex-string format and is treated as an already encoded
- * TXT data byte sequence.
+ * values) parameters are optional, and if not provided zero is used. The optional `txt` parameter sets the TXT
+ * data associated with the service. The `txt` value must be in hex-string format and is treated as an already
+ * encoded TXT data byte sequence.
* @par
* Adds a service with a given instance name, service name, and port number.
* @moreinfo{@srp}.
@@ -664,8 +663,8 @@
{
// `key [enable/disable]`
- error = Interpreter::GetInterpreter().ProcessEnableDisable(aArgs + 1, otSrpClientIsServiceKeyRecordEnabled,
- otSrpClientSetServiceKeyRecordEnabled);
+ error = ProcessEnableDisable(aArgs + 1, otSrpClientIsServiceKeyRecordEnabled,
+ otSrpClientSetServiceKeyRecordEnabled);
}
#endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
else
@@ -929,7 +928,7 @@
*/
template <> otError SrpClient::Process<Cmd("ttl")>(Arg aArgs[])
{
- return Interpreter::GetInterpreter().ProcessGetSet(aArgs, otSrpClientGetTtl, otSrpClientSetTtl);
+ return ProcessGetSet(aArgs, otSrpClientGetTtl, otSrpClientSetTtl);
}
void SrpClient::HandleCallback(otError aError,
diff --git a/src/cli/cli_srp_client.hpp b/src/cli/cli_srp_client.hpp
index 93e7fe3..85564cd 100644
--- a/src/cli/cli_srp_client.hpp
+++ b/src/cli/cli_srp_client.hpp
@@ -40,7 +40,7 @@
#include <openthread/srp_client_buffers.h>
#include "cli/cli_config.h"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE
@@ -51,11 +51,9 @@
* Implements the SRP Client CLI interpreter.
*
*/
-class SrpClient : private Output
+class SrpClient : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
diff --git a/src/cli/cli_srp_server.cpp b/src/cli/cli_srp_server.cpp
index ee61a33..5e71a4a 100644
--- a/src/cli/cli_srp_server.cpp
+++ b/src/cli/cli_srp_server.cpp
@@ -121,8 +121,7 @@
*/
template <> otError SrpServer::Process<Cmd("auto")>(Arg aArgs[])
{
- return Interpreter::GetInterpreter().ProcessEnableDisable(aArgs, otSrpServerIsAutoEnableMode,
- otSrpServerSetAutoEnableMode);
+ return ProcessEnableDisable(aArgs, otSrpServerIsAutoEnableMode, otSrpServerSetAutoEnableMode);
}
#endif
diff --git a/src/cli/cli_srp_server.hpp b/src/cli/cli_srp_server.hpp
index 344d3c2..b110eb8 100644
--- a/src/cli/cli_srp_server.hpp
+++ b/src/cli/cli_srp_server.hpp
@@ -38,7 +38,7 @@
#include <openthread/srp_server.h>
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
@@ -49,11 +49,9 @@
* Implements the SRP Server CLI interpreter.
*
*/
-class SrpServer : private Output
+class SrpServer : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
@@ -62,7 +60,7 @@
*
*/
SrpServer(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
{
}
diff --git a/src/cli/cli_tcat.cpp b/src/cli/cli_tcat.cpp
index 7845be8..1ef73a5 100644
--- a/src/cli/cli_tcat.cpp
+++ b/src/cli/cli_tcat.cpp
@@ -28,7 +28,7 @@
#include "openthread-core-config.h"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
#include "cli/cli_tcat.hpp"
diff --git a/src/cli/cli_tcat.hpp b/src/cli/cli_tcat.hpp
index 91b36cc..697cba1 100644
--- a/src/cli/cli_tcat.hpp
+++ b/src/cli/cli_tcat.hpp
@@ -33,7 +33,7 @@
#include <openthread/tcat.h>
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
@@ -45,11 +45,9 @@
* Implements the Tcat CLI interpreter.
*
*/
-class Tcat : private Output
+class Tcat : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
@@ -58,7 +56,7 @@
*
*/
Tcat(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
{
}
diff --git a/src/cli/cli_tcp.cpp b/src/cli/cli_tcp.cpp
index 90ce1aa..b891c2d 100644
--- a/src/cli/cli_tcp.cpp
+++ b/src/cli/cli_tcp.cpp
@@ -61,7 +61,7 @@
#endif
TcpExample::TcpExample(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
, mInitialized(false)
, mEndpointConnected(false)
, mEndpointConnectedFastOpen(false)
diff --git a/src/cli/cli_tcp.hpp b/src/cli/cli_tcp.hpp
index 50e7280..e658ede 100644
--- a/src/cli/cli_tcp.hpp
+++ b/src/cli/cli_tcp.hpp
@@ -49,7 +49,7 @@
#endif
#include "cli/cli_config.h"
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
#include "common/time.hpp"
namespace ot {
@@ -59,11 +59,9 @@
* Implements a CLI-based TCP example.
*
*/
-class TcpExample : private Output
+class TcpExample : private Utils
{
public:
- using Arg = Utils::CmdLineParser::Arg;
-
/**
* Constructor
*
diff --git a/src/cli/cli_udp.cpp b/src/cli/cli_udp.cpp
index 9f8afdf..299be12 100644
--- a/src/cli/cli_udp.cpp
+++ b/src/cli/cli_udp.cpp
@@ -44,7 +44,7 @@
namespace Cli {
UdpExample::UdpExample(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
, mLinkSecurityEnabled(true)
{
ClearAllBytes(mSocket);
@@ -408,7 +408,7 @@
while (!done)
{
length = sizeof(buf);
- error = Utils::CmdLineParser::ParseAsHexStringSegment(aHexString, length, buf);
+ error = ot::Utils::CmdLineParser::ParseAsHexStringSegment(aHexString, length, buf);
VerifyOrExit((error == OT_ERROR_NONE) || (error == OT_ERROR_PENDING));
done = (error == OT_ERROR_NONE);
diff --git a/src/cli/cli_udp.hpp b/src/cli/cli_udp.hpp
index 0c15c80..d1c7050 100644
--- a/src/cli/cli_udp.hpp
+++ b/src/cli/cli_udp.hpp
@@ -38,7 +38,7 @@
#include <openthread/udp.h>
-#include "cli/cli_output.hpp"
+#include "cli/cli_utils.hpp"
namespace ot {
namespace Cli {
@@ -47,11 +47,9 @@
* Implements a CLI-based UDP example.
*
*/
-class UdpExample : private Output
+class UdpExample : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
diff --git a/src/cli/cli_output.cpp b/src/cli/cli_utils.cpp
similarity index 71%
rename from src/cli/cli_output.cpp
rename to src/cli/cli_utils.cpp
index c70eacd..f2f5359 100644
--- a/src/cli/cli_output.cpp
+++ b/src/cli/cli_utils.cpp
@@ -31,7 +31,7 @@
* This file contains implementation of the CLI output module.
*/
-#include "cli_output.hpp"
+#include "cli_utils.hpp"
#include <stdio.h>
#include <stdlib.h>
@@ -48,7 +48,7 @@
namespace ot {
namespace Cli {
-const char Output::kUnknownString[] = "unknown";
+const char Utils::kUnknownString[] = "unknown";
OutputImplementer::OutputImplementer(otCliOutputCallback aCallback, void *aCallbackContext)
: mCallback(aCallback)
@@ -60,7 +60,7 @@
{
}
-void Output::OutputFormat(const char *aFormat, ...)
+void Utils::OutputFormat(const char *aFormat, ...)
{
va_list args;
@@ -69,7 +69,7 @@
va_end(args);
}
-void Output::OutputFormat(uint8_t aIndentSize, const char *aFormat, ...)
+void Utils::OutputFormat(uint8_t aIndentSize, const char *aFormat, ...)
{
va_list args;
@@ -80,7 +80,7 @@
va_end(args);
}
-void Output::OutputLine(const char *aFormat, ...)
+void Utils::OutputLine(const char *aFormat, ...)
{
va_list args;
@@ -91,7 +91,7 @@
OutputNewLine();
}
-void Output::OutputLine(uint8_t aIndentSize, const char *aFormat, ...)
+void Utils::OutputLine(uint8_t aIndentSize, const char *aFormat, ...)
{
va_list args;
@@ -104,11 +104,11 @@
OutputNewLine();
}
-void Output::OutputNewLine(void) { OutputFormat("\r\n"); }
+void Utils::OutputNewLine(void) { OutputFormat("\r\n"); }
-void Output::OutputSpaces(uint8_t aCount) { OutputFormat("%*s", aCount, ""); }
+void Utils::OutputSpaces(uint8_t aCount) { OutputFormat("%*s", aCount, ""); }
-void Output::OutputBytes(const uint8_t *aBytes, uint16_t aLength)
+void Utils::OutputBytes(const uint8_t *aBytes, uint16_t aLength)
{
for (uint16_t i = 0; i < aLength; i++)
{
@@ -116,13 +116,13 @@
}
}
-void Output::OutputBytesLine(const uint8_t *aBytes, uint16_t aLength)
+void Utils::OutputBytesLine(const uint8_t *aBytes, uint16_t aLength)
{
OutputBytes(aBytes, aLength);
OutputNewLine();
}
-const char *Output::Uint64ToString(uint64_t aUint64, Uint64StringBuffer &aBuffer)
+const char *Utils::Uint64ToString(uint64_t aUint64, Uint64StringBuffer &aBuffer)
{
char *cur = &aBuffer.mChars[Uint64StringBuffer::kSize - 1];
@@ -143,24 +143,24 @@
return cur;
}
-void Output::OutputUint64(uint64_t aUint64)
+void Utils::OutputUint64(uint64_t aUint64)
{
Uint64StringBuffer buffer;
OutputFormat("%s", Uint64ToString(aUint64, buffer));
}
-void Output::OutputUint64Line(uint64_t aUint64)
+void Utils::OutputUint64Line(uint64_t aUint64)
{
OutputUint64(aUint64);
OutputNewLine();
}
-void Output::OutputEnabledDisabledStatus(bool aEnabled) { OutputLine(aEnabled ? "Enabled" : "Disabled"); }
+void Utils::OutputEnabledDisabledStatus(bool aEnabled) { OutputLine(aEnabled ? "Enabled" : "Disabled"); }
#if OPENTHREAD_FTD || OPENTHREAD_MTD
-void Output::OutputIp6Address(const otIp6Address &aAddress)
+void Utils::OutputIp6Address(const otIp6Address &aAddress)
{
char string[OT_IP6_ADDRESS_STRING_SIZE];
@@ -169,13 +169,13 @@
return OutputFormat("%s", string);
}
-void Output::OutputIp6AddressLine(const otIp6Address &aAddress)
+void Utils::OutputIp6AddressLine(const otIp6Address &aAddress)
{
OutputIp6Address(aAddress);
OutputNewLine();
}
-void Output::OutputIp6Prefix(const otIp6Prefix &aPrefix)
+void Utils::OutputIp6Prefix(const otIp6Prefix &aPrefix)
{
char string[OT_IP6_PREFIX_STRING_SIZE];
@@ -184,25 +184,25 @@
OutputFormat("%s", string);
}
-void Output::OutputIp6PrefixLine(const otIp6Prefix &aPrefix)
+void Utils::OutputIp6PrefixLine(const otIp6Prefix &aPrefix)
{
OutputIp6Prefix(aPrefix);
OutputNewLine();
}
-void Output::OutputIp6Prefix(const otIp6NetworkPrefix &aPrefix)
+void Utils::OutputIp6Prefix(const otIp6NetworkPrefix &aPrefix)
{
OutputFormat("%x:%x:%x:%x::/64", (aPrefix.m8[0] << 8) | aPrefix.m8[1], (aPrefix.m8[2] << 8) | aPrefix.m8[3],
(aPrefix.m8[4] << 8) | aPrefix.m8[5], (aPrefix.m8[6] << 8) | aPrefix.m8[7]);
}
-void Output::OutputIp6PrefixLine(const otIp6NetworkPrefix &aPrefix)
+void Utils::OutputIp6PrefixLine(const otIp6NetworkPrefix &aPrefix)
{
OutputIp6Prefix(aPrefix);
OutputNewLine();
}
-void Output::OutputSockAddr(const otSockAddr &aSockAddr)
+void Utils::OutputSockAddr(const otSockAddr &aSockAddr)
{
char string[OT_IP6_SOCK_ADDR_STRING_SIZE];
@@ -211,13 +211,13 @@
return OutputFormat("%s", string);
}
-void Output::OutputSockAddrLine(const otSockAddr &aSockAddr)
+void Utils::OutputSockAddrLine(const otSockAddr &aSockAddr)
{
OutputSockAddr(aSockAddr);
OutputNewLine();
}
-void Output::OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength)
+void Utils::OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength)
{
otDnsTxtEntry entry;
otDnsTxtEntryIterator iterator;
@@ -262,7 +262,7 @@
OutputFormat("]");
}
-const char *Output::PercentageToString(uint16_t aValue, PercentageStringBuffer &aBuffer)
+const char *Utils::PercentageToString(uint16_t aValue, PercentageStringBuffer &aBuffer)
{
uint32_t scaledValue = aValue;
StringWriter writer(aBuffer.mChars, sizeof(aBuffer.mChars));
@@ -275,7 +275,7 @@
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
-void Output::OutputFormatV(const char *aFormat, va_list aArguments) { mImplementer.OutputV(aFormat, aArguments); }
+void Utils::OutputFormatV(const char *aFormat, va_list aArguments) { mImplementer.OutputV(aFormat, aArguments); }
void OutputImplementer::OutputV(const char *aFormat, va_list aArguments)
{
@@ -370,7 +370,7 @@
}
#if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE
-void Output::LogInput(const Arg *aArgs)
+void Utils::LogInput(const Arg *aArgs)
{
String<kInputOutputLogStringSize> inputString;
@@ -383,7 +383,7 @@
}
#endif
-void Output::OutputTableHeader(uint8_t aNumColumns, const char *const aTitles[], const uint8_t aWidths[])
+void Utils::OutputTableHeader(uint8_t aNumColumns, const char *const aTitles[], const uint8_t aWidths[])
{
for (uint8_t index = 0; index < aNumColumns; index++)
{
@@ -412,7 +412,7 @@
OutputTableSeparator(aNumColumns, aWidths);
}
-void Output::OutputTableSeparator(uint8_t aNumColumns, const uint8_t aWidths[])
+void Utils::OutputTableSeparator(uint8_t aNumColumns, const uint8_t aWidths[])
{
for (uint8_t index = 0; index < aNumColumns; index++)
{
@@ -427,5 +427,95 @@
OutputLine("+");
}
+otError Utils::ParseEnableOrDisable(const Arg &aArg, bool &aEnable)
+{
+ otError error = OT_ERROR_NONE;
+
+ if (aArg == "enable")
+ {
+ aEnable = true;
+ }
+ else if (aArg == "disable")
+ {
+ aEnable = false;
+ }
+ else
+ {
+ error = OT_ERROR_INVALID_COMMAND;
+ }
+
+ return error;
+}
+
+otError Utils::ProcessEnableDisable(Arg aArgs[], SetEnabledHandler aSetEnabledHandler)
+{
+ otError error = OT_ERROR_NONE;
+ bool enable;
+
+ if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
+ {
+ aSetEnabledHandler(GetInstancePtr(), enable);
+ }
+ else
+ {
+ error = OT_ERROR_INVALID_COMMAND;
+ }
+
+ return error;
+}
+
+otError Utils::ProcessEnableDisable(Arg aArgs[], SetEnabledHandlerFailable aSetEnabledHandler)
+{
+ otError error = OT_ERROR_NONE;
+ bool enable;
+
+ if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE)
+ {
+ error = aSetEnabledHandler(GetInstancePtr(), enable);
+ }
+ else
+ {
+ error = OT_ERROR_INVALID_COMMAND;
+ }
+
+ return error;
+}
+
+otError Utils::ProcessEnableDisable(Arg aArgs[],
+ IsEnabledHandler aIsEnabledHandler,
+ SetEnabledHandler aSetEnabledHandler)
+{
+ otError error = OT_ERROR_NONE;
+
+ if (aArgs[0].IsEmpty())
+ {
+ OutputEnabledDisabledStatus(aIsEnabledHandler(GetInstancePtr()));
+ }
+ else
+ {
+ error = ProcessEnableDisable(aArgs, aSetEnabledHandler);
+ }
+
+ return error;
+}
+
+otError Utils::ProcessEnableDisable(Arg aArgs[],
+ IsEnabledHandler aIsEnabledHandler,
+ SetEnabledHandlerFailable aSetEnabledHandler)
+{
+ otError error = OT_ERROR_NONE;
+
+ if (aArgs[0].IsEmpty())
+ {
+ OutputEnabledDisabledStatus(aIsEnabledHandler(GetInstancePtr()));
+ }
+ else
+ {
+ error = ProcessEnableDisable(aArgs, aSetEnabledHandler);
+ }
+
+ return error;
+}
+
} // namespace Cli
} // namespace ot
diff --git a/src/cli/cli_output.hpp b/src/cli/cli_utils.hpp
similarity index 75%
rename from src/cli/cli_output.hpp
rename to src/cli/cli_utils.hpp
index 3029108..ebf9cc1 100644
--- a/src/cli/cli_output.hpp
+++ b/src/cli/cli_utils.hpp
@@ -70,7 +70,7 @@
return (aString[0] == '\0') ? 0 : (static_cast<uint8_t>(aString[0]) + Cmd(aString + 1) * 255u);
}
-class Output;
+class Utils;
/**
* Implements the basic output functions.
@@ -78,7 +78,7 @@
*/
class OutputImplementer
{
- friend class Output;
+ friend class Utils;
public:
/**
@@ -111,13 +111,13 @@
};
/**
- * Provides CLI output helper methods.
+ * Provides CLI helper methods.
*
*/
-class Output
+class Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg; ///< An argument
+ typedef ot::Utils::CmdLineParser::Arg Arg; ///< An argument
/**
* Represent a CLI command table entry, mapping a command with `aName` to a handler method.
@@ -190,7 +190,7 @@
* @param[in] aImplementer An `OutputImplementer`.
*
*/
- Output(otInstance *aInstance, OutputImplementer &aImplementer)
+ Utils(otInstance *aInstance, OutputImplementer &aImplementer)
: mInstance(aInstance)
, mImplementer(aImplementer)
{
@@ -543,6 +543,108 @@
memset(reinterpret_cast<void *>(&aObject), 0, sizeof(ObjectType));
}
+ // Definitions of handlers to process Get/Set/Enable/Disable.
+ template <typename ValueType> using GetHandler = ValueType (&)(otInstance *);
+ template <typename ValueType> using SetHandler = void (&)(otInstance *, ValueType);
+ template <typename ValueType> using SetHandlerFailable = otError (&)(otInstance *, ValueType);
+ using IsEnabledHandler = bool (&)(otInstance *);
+ using SetEnabledHandler = void (&)(otInstance *, bool);
+ using SetEnabledHandlerFailable = otError (&)(otInstance *, bool);
+
+ // Returns format string to output a `ValueType` (e.g., "%u" for `uint16_t`).
+ template <typename ValueType> static constexpr const char *FormatStringFor(void);
+
+ /**
+ * Checks a given argument string against "enable" or "disable" commands.
+ *
+ * @param[in] aArg The argument string to parse.
+ * @param[out] aEnable Boolean variable to return outcome on success.
+ * Set to TRUE for "enable" command, and FALSE for "disable" command.
+ *
+ * @retval OT_ERROR_NONE Successfully parsed the @p aString and updated @p aEnable.
+ * @retval OT_ERROR_INVALID_COMMAND The @p aString is not "enable" or "disable" command.
+ *
+ */
+ static otError ParseEnableOrDisable(const Arg &aArg, bool &aEnable);
+
+ // General template implementation.
+ // Specializations for `uint32_t` and `int32_t` are added at the end.
+ template <typename ValueType> otError ProcessGet(Arg aArgs[], GetHandler<ValueType> aGetHandler)
+ {
+ static_assert(
+ TypeTraits::IsSame<ValueType, uint8_t>::kValue || TypeTraits::IsSame<ValueType, uint16_t>::kValue ||
+ TypeTraits::IsSame<ValueType, int8_t>::kValue || TypeTraits::IsSame<ValueType, int16_t>::kValue ||
+ TypeTraits::IsSame<ValueType, const char *>::kValue,
+ "ValueType must be an 8, 16 `int` or `uint` type, or a `const char *`");
+
+ otError error = OT_ERROR_NONE;
+
+ VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+ OutputLine(FormatStringFor<ValueType>(), aGetHandler(GetInstancePtr()));
+
+ exit:
+ return error;
+ }
+
+ template <typename ValueType> otError ProcessSet(Arg aArgs[], SetHandler<ValueType> aSetHandler)
+ {
+ otError error;
+ ValueType value;
+
+ SuccessOrExit(error = aArgs[0].ParseAs<ValueType>(value));
+ VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+
+ aSetHandler(GetInstancePtr(), value);
+
+ exit:
+ return error;
+ }
+
+ template <typename ValueType> otError ProcessSet(Arg aArgs[], SetHandlerFailable<ValueType> aSetHandler)
+ {
+ otError error;
+ ValueType value;
+
+ SuccessOrExit(error = aArgs[0].ParseAs<ValueType>(value));
+ VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+
+ error = aSetHandler(GetInstancePtr(), value);
+
+ exit:
+ return error;
+ }
+
+ template <typename ValueType>
+ otError ProcessGetSet(Arg aArgs[], GetHandler<ValueType> aGetHandler, SetHandler<ValueType> aSetHandler)
+ {
+ otError error = ProcessGet(aArgs, aGetHandler);
+
+ VerifyOrExit(error != OT_ERROR_NONE);
+ error = ProcessSet(aArgs, aSetHandler);
+
+ exit:
+ return error;
+ }
+
+ template <typename ValueType>
+ otError ProcessGetSet(Arg aArgs[], GetHandler<ValueType> aGetHandler, SetHandlerFailable<ValueType> aSetHandler)
+ {
+ otError error = ProcessGet(aArgs, aGetHandler);
+
+ VerifyOrExit(error != OT_ERROR_NONE);
+ error = ProcessSet(aArgs, aSetHandler);
+
+ exit:
+ return error;
+ }
+
+ otError ProcessEnableDisable(Arg aArgs[], SetEnabledHandler aSetEnabledHandler);
+ otError ProcessEnableDisable(Arg aArgs[], SetEnabledHandlerFailable aSetEnabledHandler);
+ otError ProcessEnableDisable(Arg aArgs[], IsEnabledHandler aIsEnabledHandler, SetEnabledHandler aSetEnabledHandler);
+ otError ProcessEnableDisable(Arg aArgs[],
+ IsEnabledHandler aIsEnabledHandler,
+ SetEnabledHandlerFailable aSetEnabledHandler);
+
protected:
void OutputFormatV(const char *aFormat, va_list aArguments);
@@ -562,6 +664,46 @@
OutputImplementer &mImplementer;
};
+// Specializations of `FormatStringFor<ValueType>()`
+
+template <> inline constexpr const char *Utils::FormatStringFor<uint8_t>(void) { return "%u"; }
+
+template <> inline constexpr const char *Utils::FormatStringFor<uint16_t>(void) { return "%u"; }
+
+template <> inline constexpr const char *Utils::FormatStringFor<uint32_t>(void) { return "%lu"; }
+
+template <> inline constexpr const char *Utils::FormatStringFor<int8_t>(void) { return "%d"; }
+
+template <> inline constexpr const char *Utils::FormatStringFor<int16_t>(void) { return "%d"; }
+
+template <> inline constexpr const char *Utils::FormatStringFor<int32_t>(void) { return "%ld"; }
+
+template <> inline constexpr const char *Utils::FormatStringFor<const char *>(void) { return "%s"; }
+
+// Specialization of ProcessGet<> for `uint32_t` and `int32_t`
+
+template <> inline otError Utils::ProcessGet<uint32_t>(Arg aArgs[], GetHandler<uint32_t> aGetHandler)
+{
+ otError error = OT_ERROR_NONE;
+
+ VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+ OutputLine(FormatStringFor<uint32_t>(), ToUlong(aGetHandler(GetInstancePtr())));
+
+exit:
+ return error;
+}
+
+template <> inline otError Utils::ProcessGet<int32_t>(Arg aArgs[], GetHandler<int32_t> aGetHandler)
+{
+ otError error = OT_ERROR_NONE;
+
+ VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+ OutputLine(FormatStringFor<int32_t>(), static_cast<long int>(aGetHandler(GetInstancePtr())));
+
+exit:
+ return error;
+}
+
} // namespace Cli
} // namespace ot
diff --git a/src/cli/radio.cmake b/src/cli/radio.cmake
index 449f996..47652d2 100644
--- a/src/cli/radio.cmake
+++ b/src/cli/radio.cmake
@@ -45,7 +45,7 @@
target_sources(openthread-cli-radio
PRIVATE
cli.cpp
- cli_output.cpp
+ cli_utils.cpp
)
if(NOT DEFINED OT_MBEDTLS_RCP)
diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn
index a866cd0..715741d 100644
--- a/src/core/BUILD.gn
+++ b/src/core/BUILD.gn
@@ -41,6 +41,8 @@
defines += [ "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_3" ]
} else if (openthread_config_thread_version == "1.3.1") {
defines += [ "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_3_1" ]
+ } else if (openthread_config_thread_version == "1.4") {
+ defines += [ "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_4" ]
} else if (openthread_config_thread_version != "") {
assert(false,
"Unrecognized Thread version: ${openthread_config_thread_version}")
diff --git a/src/core/api/border_agent_api.cpp b/src/core/api/border_agent_api.cpp
index 3590116..6a9f42f 100644
--- a/src/core/api/border_agent_api.cpp
+++ b/src/core/api/border_agent_api.cpp
@@ -64,4 +64,35 @@
return AsCoreType(aInstance).Get<MeshCoP::BorderAgent>().GetUdpPort();
}
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+
+otError otBorderAgentSetEphemeralKey(otInstance *aInstance,
+ const char *aKeyString,
+ uint32_t aTimeout,
+ uint16_t aUdpPort)
+{
+ AssertPointerIsNotNull(aKeyString);
+
+ return AsCoreType(aInstance).Get<MeshCoP::BorderAgent>().SetEphemeralKey(aKeyString, aTimeout, aUdpPort);
+}
+
+void otBorderAgentClearEphemeralKey(otInstance *aInstance)
+{
+ AsCoreType(aInstance).Get<MeshCoP::BorderAgent>().ClearEphemeralKey();
+}
+
+bool otBorderAgentIsEphemeralKeyActive(otInstance *aInstance)
+{
+ return AsCoreType(aInstance).Get<MeshCoP::BorderAgent>().IsEphemeralKeyActive();
+}
+
+void otBorderAgentSetEphemeralKeyCallback(otInstance *aInstance,
+ otBorderAgentEphemeralKeyCallback aCallback,
+ void *aContext)
+{
+ AsCoreType(aInstance).Get<MeshCoP::BorderAgent>().SetEphemeralKeyCallback(aCallback, aContext);
+}
+
+#endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
diff --git a/src/core/api/border_routing_api.cpp b/src/core/api/border_routing_api.cpp
index b62ee70..7c8002d 100644
--- a/src/core/api/border_routing_api.cpp
+++ b/src/core/api/border_routing_api.cpp
@@ -75,6 +75,11 @@
AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().ClearRouteInfoOptionPreference();
}
+otError otBorderRoutingSetExtraRouterAdvertOptions(otInstance *aInstance, const uint8_t *aOptions, uint16_t aLength)
+{
+ return AsCoreType(aInstance).Get<BorderRouter::RoutingManager>().SetExtraRouterAdvertOptions(aOptions, aLength);
+}
+
otRoutePreference otBorderRoutingGetRoutePreference(otInstance *aInstance)
{
return static_cast<otRoutePreference>(
diff --git a/src/core/api/channel_manager_api.cpp b/src/core/api/channel_manager_api.cpp
index af17e0d..3aa5d36 100644
--- a/src/core/api/channel_manager_api.cpp
+++ b/src/core/api/channel_manager_api.cpp
@@ -33,7 +33,9 @@
#include "openthread-core-config.h"
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+ (OPENTHREAD_FTD || \
+ (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
#include <openthread/channel_manager.h>
@@ -43,16 +45,19 @@
using namespace ot;
+#if OPENTHREAD_FTD
void otChannelManagerRequestChannelChange(otInstance *aInstance, uint8_t aChannel)
{
- AsCoreType(aInstance).Get<Utils::ChannelManager>().RequestChannelChange(aChannel);
+ AsCoreType(aInstance).Get<Utils::ChannelManager>().RequestNetworkChannelChange(aChannel);
}
+#endif
uint8_t otChannelManagerGetRequestedChannel(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<Utils::ChannelManager>().GetRequestedChannel();
}
+#if OPENTHREAD_FTD
uint16_t otChannelManagerGetDelay(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<Utils::ChannelManager>().GetDelay();
@@ -66,19 +71,39 @@
#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
otError otChannelManagerRequestChannelSelect(otInstance *aInstance, bool aSkipQualityCheck)
{
- return AsCoreType(aInstance).Get<Utils::ChannelManager>().RequestChannelSelect(aSkipQualityCheck);
+ return AsCoreType(aInstance).Get<Utils::ChannelManager>().RequestNetworkChannelSelect(aSkipQualityCheck);
}
#endif
void otChannelManagerSetAutoChannelSelectionEnabled(otInstance *aInstance, bool aEnabled)
{
- AsCoreType(aInstance).Get<Utils::ChannelManager>().SetAutoChannelSelectionEnabled(aEnabled);
+ AsCoreType(aInstance).Get<Utils::ChannelManager>().SetAutoNetworkChannelSelectionEnabled(aEnabled);
}
bool otChannelManagerGetAutoChannelSelectionEnabled(otInstance *aInstance)
{
- return AsCoreType(aInstance).Get<Utils::ChannelManager>().GetAutoChannelSelectionEnabled();
+ return AsCoreType(aInstance).Get<Utils::ChannelManager>().GetAutoNetworkChannelSelectionEnabled();
}
+#endif // OPENTHREAD_FTD
+
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
+otError otChannelManagerRequestCslChannelSelect(otInstance *aInstance, bool aSkipQualityCheck)
+{
+ return AsCoreType(aInstance).Get<Utils::ChannelManager>().RequestCslChannelSelect(aSkipQualityCheck);
+}
+#endif
+
+void otChannelManagerSetAutoCslChannelSelectionEnabled(otInstance *aInstance, bool aEnabled)
+{
+ AsCoreType(aInstance).Get<Utils::ChannelManager>().SetAutoCslChannelSelectionEnabled(aEnabled);
+}
+
+bool otChannelManagerGetAutoCslChannelSelectionEnabled(otInstance *aInstance)
+{
+ return AsCoreType(aInstance).Get<Utils::ChannelManager>().GetAutoCslChannelSelectionEnabled();
+}
+#endif
otError otChannelManagerSetAutoChannelSelectionInterval(otInstance *aInstance, uint32_t aInterval)
{
diff --git a/src/core/api/message_api.cpp b/src/core/api/message_api.cpp
index 41dedc1..fe55b90 100644
--- a/src/core/api/message_api.cpp
+++ b/src/core/api/message_api.cpp
@@ -90,6 +90,11 @@
int8_t otMessageGetRss(const otMessage *aMessage) { return AsCoreType(aMessage).GetAverageRss(); }
+otError otMessageGetThreadLinkInfo(const otMessage *aMessage, otThreadLinkInfo *aLinkInfo)
+{
+ return AsCoreType(aMessage).GetLinkInfo(AsCoreType(aLinkInfo));
+}
+
otError otMessageAppend(otMessage *aMessage, const void *aBuf, uint16_t aLength)
{
AssertPointerIsNotNull(aBuf);
diff --git a/src/core/api/thread_api.cpp b/src/core/api/thread_api.cpp
index 6bf2ca3..97e5508 100644
--- a/src/core/api/thread_api.cpp
+++ b/src/core/api/thread_api.cpp
@@ -275,15 +275,15 @@
void otThreadSetKeySequenceCounter(otInstance *aInstance, uint32_t aKeySequenceCounter)
{
- AsCoreType(aInstance).Get<KeyManager>().SetCurrentKeySequence(aKeySequenceCounter);
+ AsCoreType(aInstance).Get<KeyManager>().SetCurrentKeySequence(aKeySequenceCounter, KeyManager::kForceUpdate);
}
-uint32_t otThreadGetKeySwitchGuardTime(otInstance *aInstance)
+uint16_t otThreadGetKeySwitchGuardTime(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<KeyManager>().GetKeySwitchGuardTime();
}
-void otThreadSetKeySwitchGuardTime(otInstance *aInstance, uint32_t aKeySwitchGuardTime)
+void otThreadSetKeySwitchGuardTime(otInstance *aInstance, uint16_t aKeySwitchGuardTime)
{
AsCoreType(aInstance).Get<KeyManager>().SetKeySwitchGuardTime(aKeySwitchGuardTime);
}
diff --git a/src/core/backbone_router/bbr_manager.cpp b/src/core/backbone_router/bbr_manager.cpp
index 551cfa2..4fb15e4 100644
--- a/src/core/backbone_router/bbr_manager.cpp
+++ b/src/core/backbone_router/bbr_manager.cpp
@@ -40,6 +40,7 @@
#include "common/locator_getters.hpp"
#include "common/log.hpp"
#include "common/num_utils.hpp"
+#include "common/numeric_limits.hpp"
#include "common/random.hpp"
#include "instance/instance.hpp"
#include "thread/mle_types.hpp"
@@ -202,7 +203,7 @@
}
else
{
- VerifyOrExit(timeout < UINT32_MAX, status = ThreadStatusTlv::kMlrNoPersistent);
+ VerifyOrExit(timeout < NumericLimits<uint32_t>::kMax, status = ThreadStatusTlv::kMlrNoPersistent);
if (timeout != 0)
{
diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp
index 81f2d9a..bebd795 100644
--- a/src/core/border_router/routing_manager.cpp
+++ b/src/core/border_router/routing_manager.cpp
@@ -363,6 +363,22 @@
return;
}
+Error RoutingManager::SetExtraRouterAdvertOptions(const uint8_t *aOptions, uint16_t aLength)
+{
+ Error error = kErrorNone;
+
+ if (aOptions == nullptr)
+ {
+ mExtraRaOptions.Free();
+ }
+ else
+ {
+ error = mExtraRaOptions.SetFrom(aOptions, aLength);
+ }
+
+ return error;
+}
+
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
void RoutingManager::HandleSrpServerAutoEnableMode(void)
{
@@ -478,7 +494,10 @@
mNat64PrefixManager.Evaluate();
#endif
- SendRouterAdvertisement(kAdvPrefixesFromNetData);
+ if (IsInitalPolicyEvaluationDone())
+ {
+ SendRouterAdvertisement(kAdvPrefixesFromNetData);
+ }
#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
if (Get<Srp::Server>().IsAutoEnableMode() && IsInitalPolicyEvaluationDone())
@@ -568,35 +587,24 @@
void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode)
{
- // RA message max length is derived to accommodate:
- //
- // - The RA header.
- // - One RA Flags Extensions Option (with stub router flag).
- // - One PIO for current local on-link prefix.
- // - At most `kMaxOldPrefixes` for old deprecating on-link prefixes.
- // - At most 3 times `kMaxOnMeshPrefixes` RIO for on-mesh prefixes.
- // Factor three is used for RIOs to account for any new prefix
- // with older prefixes entries being deprecated and prefixes
- // being invalidated.
-
- static constexpr uint16_t kMaxRaLength =
- sizeof(Ip6::Nd::RouterAdvertMessage::Header) + sizeof(Ip6::Nd::RaFlagsExtOption) +
- sizeof(Ip6::Nd::PrefixInfoOption) + sizeof(Ip6::Nd::PrefixInfoOption) * OnLinkPrefixManager::kMaxOldPrefixes +
- 3 * kMaxOnMeshPrefixes * (sizeof(Ip6::Nd::RouteInfoOption) + sizeof(Ip6::Prefix));
-
- uint8_t buffer[kMaxRaLength];
- Ip6::Nd::RouterAdvertMessage raMsg(mRaInfo.mHeader, buffer);
+ Error error = kErrorNone;
+ RouterAdvert::TxMessage raMsg;
+ RouterAdvert::Header header;
+ Ip6::Address destAddress;
+ InfraIf::Icmp6Packet packet;
LogInfo("Preparing RA");
- mDiscoveredPrefixTable.DetermineAndSetFlags(raMsg);
+ header = mRaInfo.mHeader;
+ mDiscoveredPrefixTable.DetermineAndSetFlags(header);
- LogInfo("- RA Header - flags - M:%u O:%u", raMsg.GetHeader().IsManagedAddressConfigFlagSet(),
- raMsg.GetHeader().IsOtherConfigFlagSet());
- LogInfo("- RA Header - default route - lifetime:%u", raMsg.GetHeader().GetRouterLifetime());
+ SuccessOrExit(error = raMsg.AppendHeader(header));
+
+ LogInfo("- RA Header - flags - M:%u O:%u", header.IsManagedAddressConfigFlagSet(), header.IsOtherConfigFlagSet());
+ LogInfo("- RA Header - default route - lifetime:%u", header.GetRouterLifetime());
#if OPENTHREAD_CONFIG_BORDER_ROUTING_STUB_ROUTER_FLAG_IN_EMITTED_RA_ENABLE
- SuccessOrAssert(raMsg.AppendFlagsExtensionOption(/* aStubRouterFlag */ true));
+ SuccessOrExit(error = raMsg.AppendFlagsExtensionOption(/* aStubRouterFlag */ true));
LogInfo("- FlagsExt - StubRouter:1");
#endif
@@ -604,109 +612,42 @@
// advertised or deprecated and for old prefix if is being
// deprecated.
- mOnLinkPrefixManager.AppendAsPiosTo(raMsg);
+ SuccessOrExit(error = mOnLinkPrefixManager.AppendAsPiosTo(raMsg));
if (aRaTxMode == kInvalidateAllPrevPrefixes)
{
- mRioAdvertiser.InvalidatPrevRios(raMsg);
+ SuccessOrExit(error = mRioAdvertiser.InvalidatPrevRios(raMsg));
}
else
{
- mRioAdvertiser.AppendRios(raMsg);
+ SuccessOrExit(error = mRioAdvertiser.AppendRios(raMsg));
}
- if (raMsg.ContainsAnyOptions())
+ if (mExtraRaOptions.GetLength() > 0)
{
- Error error;
- Ip6::Address destAddress;
-
- ++mRaInfo.mTxCount;
-
- destAddress.SetToLinkLocalAllNodesMulticast();
-
- error = mInfraIf.Send(raMsg.GetAsPacket(), destAddress);
-
- if (error == kErrorNone)
- {
- mRaInfo.mLastTxTime = TimerMilli::GetNow();
- Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxSuccess++;
- LogInfo("Sent RA on %s", mInfraIf.ToString().AsCString());
- DumpDebg("[BR-CERT] direction=send | type=RA |", raMsg.GetAsPacket().GetBytes(),
- raMsg.GetAsPacket().GetLength());
- }
- else
- {
- Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxFailure++;
- LogWarn("Failed to send RA on %s: %s", mInfraIf.ToString().AsCString(), ErrorToString(error));
- }
- }
-}
-
-bool RoutingManager::IsReceivedRouterAdvertFromManager(const Ip6::Nd::RouterAdvertMessage &aRaMessage) const
-{
- // Determines whether or not a received RA message was prepared by
- // by `RoutingManager` itself.
-
- bool isFromManager = false;
- uint16_t rioCount = 0;
- Ip6::Prefix prefix;
-
- VerifyOrExit(aRaMessage.ContainsAnyOptions());
-
- for (const Ip6::Nd::Option &option : aRaMessage)
- {
- switch (option.GetType())
- {
- case Ip6::Nd::Option::kTypePrefixInfo:
- {
- const Ip6::Nd::PrefixInfoOption &pio = static_cast<const Ip6::Nd::PrefixInfoOption &>(option);
-
- VerifyOrExit(pio.IsValid());
- pio.GetPrefix(prefix);
-
- // If it is a non-deprecated PIO, it should match the
- // local on-link prefix.
-
- if (pio.GetPreferredLifetime() > 0)
- {
- VerifyOrExit(prefix == mOnLinkPrefixManager.GetLocalPrefix());
- }
-
- break;
- }
-
- case Ip6::Nd::Option::kTypeRouteInfo:
- {
- // RIO (with non-zero lifetime) should match entries from
- // `mRioAdvertiser`. We keep track of the number of matched
- // RIOs and check after the loop ends that all entries were
- // seen.
-
- const Ip6::Nd::RouteInfoOption &rio = static_cast<const Ip6::Nd::RouteInfoOption &>(option);
-
- VerifyOrExit(rio.IsValid());
- rio.GetPrefix(prefix);
-
- if (rio.GetRouteLifetime() != 0)
- {
- VerifyOrExit(mRioAdvertiser.HasAdvertised(prefix));
- rioCount++;
- }
-
- break;
- }
-
- default:
- ExitNow();
- }
+ SuccessOrExit(error = raMsg.AppendBytes(mExtraRaOptions.GetBytes(), mExtraRaOptions.GetLength()));
}
- VerifyOrExit(rioCount == mRioAdvertiser.GetAdvertisedRioCount());
+ VerifyOrExit(raMsg.ContainsAnyOptions());
- isFromManager = true;
+ destAddress.SetToLinkLocalAllNodesMulticast();
+ raMsg.GetAsPacket(packet);
+
+ mRaInfo.IncrementTxCountAndSaveHash(packet);
+
+ SuccessOrExit(error = mInfraIf.Send(packet, destAddress));
+
+ mRaInfo.mLastTxTime = TimerMilli::GetNow();
+ Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxSuccess++;
+ LogInfo("Sent RA on %s", mInfraIf.ToString().AsCString());
+ DumpDebg("[BR-CERT] direction=send | type=RA |", packet.GetBytes(), packet.GetLength());
exit:
- return isFromManager;
+ if (error != kErrorNone)
+ {
+ Get<Ip6::Ip6>().GetBorderRoutingCounters().mRaTxFailure++;
+ LogWarn("Failed to send RA on %s: %s", mInfraIf.ToString().AsCString(), ErrorToString(error));
+ }
}
bool RoutingManager::IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix)
@@ -726,7 +667,7 @@
return (aPrefix.GetLength() == kOmrPrefixLength) && !aPrefix.IsLinkLocal() && !aPrefix.IsMulticast();
}
-bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption &aPio)
+bool RoutingManager::IsValidOnLinkPrefix(const PrefixInfoOption &aPio)
{
Ip6::Prefix prefix;
@@ -781,10 +722,10 @@
void RoutingManager::HandleNeighborAdvertisement(const InfraIf::Icmp6Packet &aPacket)
{
- const Ip6::Nd::NeighborAdvertMessage *naMsg;
+ const NeighborAdvertMessage *naMsg;
VerifyOrExit(aPacket.GetLength() >= sizeof(naMsg));
- naMsg = reinterpret_cast<const Ip6::Nd::NeighborAdvertMessage *>(aPacket.GetBytes());
+ naMsg = reinterpret_cast<const NeighborAdvertMessage *>(aPacket.GetBytes());
mDiscoveredPrefixTable.ProcessNeighborAdvertMessage(*naMsg);
@@ -794,7 +735,7 @@
void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress)
{
- Ip6::Nd::RouterAdvertMessage routerAdvMessage(aPacket);
+ RouterAdvert::RxMessage routerAdvMessage(aPacket);
OT_ASSERT(mIsRunning);
@@ -818,7 +759,7 @@
return;
}
-bool RoutingManager::ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix)
+bool RoutingManager::ShouldProcessPrefixInfoOption(const PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix)
{
// Indicate whether to process or skip a given prefix
// from a PIO (from received RA message).
@@ -844,7 +785,7 @@
return shouldProcess;
}
-bool RoutingManager::ShouldProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, const Ip6::Prefix &aPrefix)
+bool RoutingManager::ShouldProcessRouteInfoOption(const RouteInfoOption &aRio, const Ip6::Prefix &aPrefix)
{
// Indicate whether to process or skip a given prefix
// from a RIO (from received RA message).
@@ -943,18 +884,18 @@
return contains;
}
-void RoutingManager::UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage)
+void RoutingManager::UpdateRouterAdvertHeader(const RouterAdvert::RxMessage *aRouterAdvertMessage)
{
// Updates the `mRaInfo` from the given RA message.
- Ip6::Nd::RouterAdvertMessage::Header oldHeader;
+ RouterAdvert::Header oldHeader;
if (aRouterAdvertMessage != nullptr)
{
// We skip and do not update RA header if the received RA message
// was not prepared and sent by `RoutingManager` itself.
- VerifyOrExit(!IsReceivedRouterAdvertFromManager(*aRouterAdvertMessage));
+ VerifyOrExit(!mRaInfo.IsRaFromManager(*aRouterAdvertMessage));
}
oldHeader = mRaInfo.mHeader;
@@ -1057,8 +998,8 @@
{
}
-void RoutingManager::DiscoveredPrefixTable::ProcessRouterAdvertMessage(const Ip6::Nd::RouterAdvertMessage &aRaMessage,
- const Ip6::Address &aSrcAddress)
+void RoutingManager::DiscoveredPrefixTable::ProcessRouterAdvertMessage(const RouterAdvert::RxMessage &aRaMessage,
+ const Ip6::Address &aSrcAddress)
{
// Process a received RA message and update the prefix table.
@@ -1088,20 +1029,20 @@
ProcessRaHeader(aRaMessage.GetHeader(), *router);
- for (const Ip6::Nd::Option &option : aRaMessage)
+ for (const Option &option : aRaMessage)
{
switch (option.GetType())
{
- case Ip6::Nd::Option::kTypePrefixInfo:
- ProcessPrefixInfoOption(static_cast<const Ip6::Nd::PrefixInfoOption &>(option), *router);
+ case Option::kTypePrefixInfo:
+ ProcessPrefixInfoOption(static_cast<const PrefixInfoOption &>(option), *router);
break;
- case Ip6::Nd::Option::kTypeRouteInfo:
- ProcessRouteInfoOption(static_cast<const Ip6::Nd::RouteInfoOption &>(option), *router);
+ case Option::kTypeRouteInfo:
+ ProcessRouteInfoOption(static_cast<const RouteInfoOption &>(option), *router);
break;
- case Ip6::Nd::Option::kTypeRaFlagsExtension:
- ProcessRaFlagsExtOption(static_cast<const Ip6::Nd::RaFlagsExtOption &>(option), *router);
+ case Option::kTypeRaFlagsExtension:
+ ProcessRaFlagsExtOption(static_cast<const RaFlagsExtOption &>(option), *router);
break;
default:
@@ -1117,8 +1058,7 @@
return;
}
-void RoutingManager::DiscoveredPrefixTable::ProcessRaHeader(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader,
- Router &aRouter)
+void RoutingManager::DiscoveredPrefixTable::ProcessRaHeader(const RouterAdvert::Header &aRaHeader, Router &aRouter)
{
Entry *entry;
Ip6::Prefix prefix;
@@ -1160,8 +1100,7 @@
return;
}
-void RoutingManager::DiscoveredPrefixTable::ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio,
- Router &aRouter)
+void RoutingManager::DiscoveredPrefixTable::ProcessPrefixInfoOption(const PrefixInfoOption &aPio, Router &aRouter)
{
Ip6::Prefix prefix;
Entry *entry;
@@ -1206,8 +1145,7 @@
return;
}
-void RoutingManager::DiscoveredPrefixTable::ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio,
- Router &aRouter)
+void RoutingManager::DiscoveredPrefixTable::ProcessRouteInfoOption(const RouteInfoOption &aRio, Router &aRouter)
{
Ip6::Prefix prefix;
Entry *entry;
@@ -1249,8 +1187,8 @@
return;
}
-void RoutingManager::DiscoveredPrefixTable::ProcessRaFlagsExtOption(const Ip6::Nd::RaFlagsExtOption &aRaFlagsOption,
- Router &aRouter)
+void RoutingManager::DiscoveredPrefixTable::ProcessRaFlagsExtOption(const RaFlagsExtOption &aRaFlagsOption,
+ Router &aRouter)
{
VerifyOrExit(aRaFlagsOption.IsValid());
aRouter.mStubRouterFlag = aRaFlagsOption.IsStubRouterFlagSet();
@@ -1560,8 +1498,7 @@
void RoutingManager::DiscoveredPrefixTable::SignalTableChanged(void) { mSignalTask.Post(); }
-void RoutingManager::DiscoveredPrefixTable::ProcessNeighborAdvertMessage(
- const Ip6::Nd::NeighborAdvertMessage &aNaMessage)
+void RoutingManager::DiscoveredPrefixTable::ProcessNeighborAdvertMessage(const NeighborAdvertMessage &aNaMessage)
{
Router *router;
@@ -1640,8 +1577,8 @@
void RoutingManager::DiscoveredPrefixTable::SendNeighborSolicitToRouter(const Router &aRouter)
{
- InfraIf::Icmp6Packet packet;
- Ip6::Nd::NeighborSolicitMessage neighborSolicitMsg;
+ InfraIf::Icmp6Packet packet;
+ NeighborSolicitMessage neighborSolicitMsg;
VerifyOrExit(!Get<RoutingManager>().mRsSender.IsInProgress());
@@ -1657,10 +1594,10 @@
return;
}
-void RoutingManager::DiscoveredPrefixTable::DetermineAndSetFlags(Ip6::Nd::RouterAdvertMessage &aRaMessage) const
+void RoutingManager::DiscoveredPrefixTable::DetermineAndSetFlags(RouterAdvert::Header &aHeader) const
{
// Determine the `M` and `O` flags to include in the RA message
- // header `aRaMessage` to be emitted.
+ // 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`
@@ -1683,12 +1620,12 @@
if (router.mManagedAddressConfigFlag)
{
- aRaMessage.GetHeader().SetManagedAddressConfigFlag();
+ aHeader.SetManagedAddressConfigFlag();
}
if (router.mOtherConfigFlag)
{
- aRaMessage.GetHeader().SetOtherConfigFlag();
+ aHeader.SetOtherConfigFlag();
}
}
}
@@ -1775,7 +1712,7 @@
//---------------------------------------------------------------------------------------------------------------------
// DiscoveredPrefixTable::Entry
-void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader)
+void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const RouterAdvert::Header &aRaHeader)
{
mPrefix.Clear();
mType = kTypeRoute;
@@ -1784,7 +1721,7 @@
mLastUpdateTime = TimerMilli::GetNow();
}
-void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::PrefixInfoOption &aPio)
+void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const PrefixInfoOption &aPio)
{
aPio.GetPrefix(mPrefix);
mType = kTypeOnLink;
@@ -1793,7 +1730,7 @@
mLastUpdateTime = TimerMilli::GetNow();
}
-void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::RouteInfoOption &aRio)
+void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const RouteInfoOption &aRio)
{
aRio.GetPrefix(mPrefix);
mType = kTypeRoute;
@@ -2525,13 +2462,18 @@
return (GetState() == kPublishing) || (GetState() == kAdvertising);
}
-void RoutingManager::OnLinkPrefixManager::AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage)
+Error RoutingManager::OnLinkPrefixManager::AppendAsPiosTo(RouterAdvert::TxMessage &aRaMessage)
{
- AppendCurPrefix(aRaMessage);
- AppendOldPrefixes(aRaMessage);
+ Error error;
+
+ SuccessOrExit(error = AppendCurPrefix(aRaMessage));
+ error = AppendOldPrefixes(aRaMessage);
+
+exit:
+ return error;
}
-void RoutingManager::OnLinkPrefixManager::AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage)
+Error RoutingManager::OnLinkPrefixManager::AppendCurPrefix(RouterAdvert::TxMessage &aRaMessage)
{
// Append the local on-link prefix to the `aRaMessage` as a PIO
// only if it is being advertised or deprecated.
@@ -2540,6 +2482,7 @@
// If in `kDeprecating` state, we include it as PIO with zero
// preferred lifetime and the remaining valid lifetime.
+ Error error = kErrorNone;
uint32_t validLifetime = kDefaultOnLinkPrefixLifetime;
uint32_t preferredLifetime = kDefaultOnLinkPrefixLifetime;
TimeMilli now = TimerMilli::GetNow();
@@ -2561,17 +2504,18 @@
ExitNow();
}
- SuccessOrAssert(aRaMessage.AppendPrefixInfoOption(mLocalPrefix, validLifetime, preferredLifetime));
+ SuccessOrExit(error = aRaMessage.AppendPrefixInfoOption(mLocalPrefix, validLifetime, preferredLifetime));
LogPrefixInfoOption(mLocalPrefix, validLifetime, preferredLifetime);
exit:
- return;
+ return error;
}
-void RoutingManager::OnLinkPrefixManager::AppendOldPrefixes(Ip6::Nd::RouterAdvertMessage &aRaMessage)
+Error RoutingManager::OnLinkPrefixManager::AppendOldPrefixes(RouterAdvert::TxMessage &aRaMessage)
{
- TimeMilli now = TimerMilli::GetNow();
+ Error error = kErrorNone;
+ TimeMilli now = TimerMilli::GetNow();
uint32_t validLifetime;
for (const OldPrefix &oldPrefix : mOldLocalPrefixes)
@@ -2582,10 +2526,13 @@
}
validLifetime = TimeMilli::MsecToSec(oldPrefix.mExpireTime - now);
- SuccessOrAssert(aRaMessage.AppendPrefixInfoOption(oldPrefix.mPrefix, validLifetime, 0));
+ SuccessOrExit(error = aRaMessage.AppendPrefixInfoOption(oldPrefix.mPrefix, validLifetime, 0));
LogPrefixInfoOption(oldPrefix.mPrefix, validLifetime, 0);
}
+
+exit:
+ return error;
}
void RoutingManager::OnLinkPrefixManager::HandleNetDataChange(void)
@@ -2821,11 +2768,13 @@
return;
}
-void RoutingManager::RioAdvertiser::InvalidatPrevRios(Ip6::Nd::RouterAdvertMessage &aRaMessage)
+Error RoutingManager::RioAdvertiser::InvalidatPrevRios(RouterAdvert::TxMessage &aRaMessage)
{
+ Error error = kErrorNone;
+
for (const RioPrefix &prefix : mPrefixes)
{
- AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage);
+ SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage));
}
#if OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE
@@ -2834,10 +2783,14 @@
mPrefixes.Clear();
mTimer.Stop();
+
+exit:
+ return error;
}
-void RoutingManager::RioAdvertiser::AppendRios(Ip6::Nd::RouterAdvertMessage &aRaMessage)
+Error RoutingManager::RioAdvertiser::AppendRios(RouterAdvert::TxMessage &aRaMessage)
{
+ Error error = kErrorNone;
TimeMilli now = TimerMilli::GetNow();
TimeMilli nextTime = now.GetDistantFuture();
RioPrefixArray oldPrefixes;
@@ -2925,7 +2878,7 @@
{
if (now >= prefix.mExpirationTime)
{
- AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage);
+ SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage));
continue;
}
}
@@ -2938,7 +2891,7 @@
if (mPrefixes.PushBack(prefix) != kErrorNone)
{
LogWarn("Too many deprecating on-mesh prefixes, removing %s", prefix.mPrefix.ToString().AsCString());
- AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage);
+ SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage));
}
nextTime = Min(nextTime, prefix.mExpirationTime);
@@ -2955,21 +2908,29 @@
lifetime = TimeMilli::MsecToSec(prefix.mExpirationTime - now);
}
- AppendRio(prefix.mPrefix, lifetime, aRaMessage);
+ SuccessOrExit(error = AppendRio(prefix.mPrefix, lifetime, aRaMessage));
}
if (nextTime != now.GetDistantFuture())
{
mTimer.FireAtIfEarlier(nextTime);
}
+
+exit:
+ return error;
}
-void RoutingManager::RioAdvertiser::AppendRio(const Ip6::Prefix &aPrefix,
- uint32_t aRouteLifetime,
- Ip6::Nd::RouterAdvertMessage &aRaMessage)
+Error RoutingManager::RioAdvertiser::AppendRio(const Ip6::Prefix &aPrefix,
+ uint32_t aRouteLifetime,
+ RouterAdvert::TxMessage &aRaMessage)
{
- SuccessOrAssert(aRaMessage.AppendRouteInfoOption(aPrefix, aRouteLifetime, mPreference));
+ Error error;
+
+ SuccessOrExit(error = aRaMessage.AppendRouteInfoOption(aPrefix, aRouteLifetime, mPreference));
LogRouteInfoOption(aPrefix, aRouteLifetime, mPreference);
+
+exit:
+ return error;
}
void RoutingManager::RioAdvertiser::HandleTimer(void)
@@ -3445,6 +3406,66 @@
#endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE
//---------------------------------------------------------------------------------------------------------------------
+// RaInfo
+
+void RoutingManager::RaInfo::IncrementTxCountAndSaveHash(const InfraIf::Icmp6Packet &aRaMessage)
+{
+ mTxCount++;
+ mLastHashIndex++;
+
+ if (mLastHashIndex == kNumHashEntries)
+ {
+ mLastHashIndex = 0;
+ }
+
+ CalculateHash(aRaMessage, mHashes[mLastHashIndex]);
+}
+
+bool RoutingManager::RaInfo::IsRaFromManager(const Ip6::Nd::RouterAdvert::RxMessage &aRaMessage) const
+{
+ // Determines whether or not a received RA message was prepared by
+ // by `RoutingManager` itself (is present in the saved `mHashes`).
+
+ bool isFromManager = false;
+ uint16_t hashIndex = mLastHashIndex;
+ uint32_t count = Min<uint32_t>(mTxCount, kNumHashEntries);
+ Hash hash;
+
+ CalculateHash(aRaMessage.GetAsPacket(), hash);
+
+ for (; count > 0; count--)
+ {
+ if (mHashes[hashIndex] == hash)
+ {
+ isFromManager = true;
+ break;
+ }
+
+ // Go to the previous index (ring buffer)
+
+ if (hashIndex == 0)
+ {
+ hashIndex = kNumHashEntries - 1;
+ }
+ else
+ {
+ hashIndex--;
+ }
+ }
+
+ return isFromManager;
+}
+
+void RoutingManager::RaInfo::CalculateHash(const InfraIf::Icmp6Packet &aRaMessage, Hash &aHash)
+{
+ Crypto::Sha256 sha256;
+
+ sha256.Start();
+ sha256.Update(aRaMessage.GetBytes(), aRaMessage.GetLength());
+ sha256.Finish(aHash);
+}
+
+//---------------------------------------------------------------------------------------------------------------------
// RsSender
RoutingManager::RsSender::RsSender(Instance &aInstance)
@@ -3476,10 +3497,10 @@
Error RoutingManager::RsSender::SendRs(void)
{
- Ip6::Address destAddress;
- Ip6::Nd::RouterSolicitMessage routerSolicit;
- InfraIf::Icmp6Packet packet;
- Error error;
+ Ip6::Address destAddress;
+ RouterSolicitMessage routerSolicit;
+ InfraIf::Icmp6Packet packet;
+ Error error;
packet.InitFrom(routerSolicit);
destAddress.SetToLinkLocalAllRoutersMulticast();
@@ -3652,12 +3673,12 @@
void RoutingManager::PdPrefixManager::ProcessPlatformGeneratedRa(const uint8_t *aRouterAdvert, const uint16_t aLength)
{
- Error error = kErrorNone;
- Ip6::Nd::RouterAdvertMessage::Icmp6Packet packet;
+ Error error = kErrorNone;
+ RouterAdvert::Icmp6Packet packet;
VerifyOrExit(IsRunning(), LogWarn("Ignore platform generated RA since PD is disabled or not running."));
packet.Init(aRouterAdvert, aLength);
- error = Process(Ip6::Nd::RouterAdvertMessage(packet));
+ error = Process(RouterAdvert::RxMessage(packet));
mNumPlatformRaReceived++;
mLastPlatformRaTime = TimerMilli::GetNow();
@@ -3668,7 +3689,7 @@
}
}
-Error RoutingManager::PdPrefixManager::Process(const Ip6::Nd::RouterAdvertMessage &aMessage)
+Error RoutingManager::PdPrefixManager::Process(const RouterAdvert::RxMessage &aMessage)
{
Error error = kErrorNone;
DiscoveredPrefixTable::Entry favoredEntry;
@@ -3677,17 +3698,17 @@
VerifyOrExit(aMessage.IsValid(), error = kErrorParse);
favoredEntry.Clear();
- for (const Ip6::Nd::Option &option : aMessage)
+ for (const Option &option : aMessage)
{
DiscoveredPrefixTable::Entry entry;
- if (option.GetType() != Ip6::Nd::Option::Type::kTypePrefixInfo ||
- !static_cast<const Ip6::Nd::PrefixInfoOption &>(option).IsValid())
+ if (option.GetType() != Option::kTypePrefixInfo || !static_cast<const PrefixInfoOption &>(option).IsValid())
{
continue;
}
+
mNumPlatformPioProcessed++;
- entry.SetFrom(static_cast<const Ip6::Nd::PrefixInfoOption &>(option));
+ entry.SetFrom(static_cast<const PrefixInfoOption &>(option));
if (!IsValidPdPrefix(entry.GetPrefix()))
{
diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp
index 4aef01d..37de6ec 100644
--- a/src/core/border_router/routing_manager.hpp
+++ b/src/core/border_router/routing_manager.hpp
@@ -55,6 +55,7 @@
#include "common/error.hpp"
#include "common/heap_allocatable.hpp"
#include "common/heap_array.hpp"
+#include "common/heap_data.hpp"
#include "common/linked_list.hpp"
#include "common/locator.hpp"
#include "common/message.hpp"
@@ -62,6 +63,7 @@
#include "common/pool.hpp"
#include "common/string.hpp"
#include "common/timer.hpp"
+#include "crypto/sha256.hpp"
#include "net/ip6.hpp"
#include "net/nat64_translator.hpp"
#include "net/nd6.hpp"
@@ -233,6 +235,22 @@
void ClearRouteInfoOptionPreference(void) { mRioAdvertiser.ClearPreference(); }
/**
+ * Sets additional options to append at the end of emitted Router Advertisement (RA) messages.
+ *
+ * The content of @p aOptions is copied internally, so can be a temporary stack variable.
+ *
+ * Subsequent calls to this method will overwrite the previously set value.
+ *
+ * @param[in] aOptions A pointer to the encoded options. Can be `nullptr` to clear.
+ * @param[in] aLength Number of bytes in @p aOptions.
+ *
+ * @retval kErrorNone Successfully set the extra option bytes.
+ * @retval kErrorNoBufs Could not allocate buffer to save the buffer.
+ *
+ */
+ Error SetExtraRouterAdvertOptions(const uint8_t *aOptions, uint16_t aLength);
+
+ /**
* Gets the current preference used for published routes in Network Data.
*
* The preference is determined as follows:
@@ -581,6 +599,15 @@
static_assert(kPolicyEvaluationMaxDelay > kPolicyEvaluationMinDelay,
"kPolicyEvaluationMaxDelay must be larger than kPolicyEvaluationMinDelay");
+ using Option = Ip6::Nd::Option;
+ using PrefixInfoOption = Ip6::Nd::PrefixInfoOption;
+ using RouteInfoOption = Ip6::Nd::RouteInfoOption;
+ using RaFlagsExtOption = Ip6::Nd::RaFlagsExtOption;
+ using RouterAdvert = Ip6::Nd::RouterAdvert;
+ using NeighborAdvertMessage = Ip6::Nd::NeighborAdvertMessage;
+ using NeighborSolicitMessage = Ip6::Nd::NeighborSolicitMessage;
+ using RouterSolicitMessage = Ip6::Nd::RouterSolicitMessage;
+
enum RouterAdvTxMode : uint8_t // Used in `SendRouterAdvertisement()`
{
kInvalidateAllPrevPrefixes,
@@ -630,9 +657,8 @@
public:
explicit DiscoveredPrefixTable(Instance &aInstance);
- void ProcessRouterAdvertMessage(const Ip6::Nd::RouterAdvertMessage &aRaMessage,
- const Ip6::Address &aSrcAddress);
- void ProcessNeighborAdvertMessage(const Ip6::Nd::NeighborAdvertMessage &aNaMessage);
+ void ProcessRouterAdvertMessage(const RouterAdvert::RxMessage &aRaMessage, const Ip6::Address &aSrcAddress);
+ void ProcessNeighborAdvertMessage(const NeighborAdvertMessage &aNaMessage);
bool ContainsDefaultOrNonUlaRoutePrefix(void) const;
bool ContainsNonUlaOnLinkPrefix(void) const;
@@ -648,7 +674,7 @@
TimeMilli CalculateNextStaleTime(TimeMilli aNow) const;
- void DetermineAndSetFlags(Ip6::Nd::RouterAdvertMessage &aRaMessage) const;
+ void DetermineAndSetFlags(RouterAdvert::Header &aHeader) const;
void InitIterator(PrefixTableIterator &aIterator) const;
Error GetNextEntry(PrefixTableIterator &aIterator, PrefixTableEntry &aEntry) const;
@@ -724,9 +750,9 @@
TimeMilli mNow;
};
- void SetFrom(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader);
- void SetFrom(const Ip6::Nd::PrefixInfoOption &aPio);
- void SetFrom(const Ip6::Nd::RouteInfoOption &aRio);
+ void SetFrom(const RouterAdvert::Header &aRaHeader);
+ void SetFrom(const PrefixInfoOption &aPio);
+ void SetFrom(const RouteInfoOption &aRio);
Type GetType(void) const { return mType; }
bool IsOnLinkPrefix(void) const { return (mType == kTypeOnLink); }
bool IsRoutePrefix(void) const { return (mType == kTypeRoute); }
@@ -824,10 +850,10 @@
void SetInitTime(void) { mData32 = TimerMilli::GetNow().GetValue(); }
};
- void ProcessRaHeader(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader, Router &aRouter);
- void ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, Router &aRouter);
- void ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, Router &aRouter);
- void ProcessRaFlagsExtOption(const Ip6::Nd::RaFlagsExtOption &aFlagsOption, Router &aRouter);
+ void ProcessRaHeader(const RouterAdvert::Header &aRaHeader, Router &aRouter);
+ void ProcessPrefixInfoOption(const PrefixInfoOption &aPio, Router &aRouter);
+ void ProcessRouteInfoOption(const RouteInfoOption &aRio, Router &aRouter);
+ void ProcessRaFlagsExtOption(const RaFlagsExtOption &aFlagsOption, Router &aRouter);
bool Contains(const Entry::Checker &aChecker) const;
void RemovePrefix(const Entry::Matcher &aMatcher);
void RemoveOrDeprecateEntriesFromInactiveRouters(void);
@@ -951,7 +977,7 @@
bool IsInitalEvaluationDone(void) const;
void HandleDiscoveredPrefixTableChanged(void);
bool ShouldPublishUlaRoute(void) const;
- void AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage);
+ Error AppendAsPiosTo(RouterAdvert::TxMessage &aRaMessage);
bool IsPublishingOrAdvertising(void) const;
void HandleNetDataChange(void);
void HandleExtPanIdChange(void);
@@ -980,8 +1006,8 @@
void PublishAndAdvertise(void);
void Deprecate(void);
void ResetExpireTime(TimeMilli aNow);
- void AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage);
- void AppendOldPrefixes(Ip6::Nd::RouterAdvertMessage &aRaMessage);
+ Error AppendCurPrefix(RouterAdvert::TxMessage &aRaMessage);
+ Error AppendOldPrefixes(RouterAdvert::TxMessage &aRaMessage);
void DeprecateOldPrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime);
void SavePrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime);
@@ -1013,8 +1039,8 @@
void SetPreference(RoutePreference aPreference);
void ClearPreference(void);
void HandleRoleChanged(void);
- void AppendRios(Ip6::Nd::RouterAdvertMessage &aRaMessage);
- void InvalidatPrevRios(Ip6::Nd::RouterAdvertMessage &aRaMessage);
+ Error AppendRios(RouterAdvert::TxMessage &aRaMessage);
+ Error InvalidatPrevRios(RouterAdvert::TxMessage &aRaMessage);
bool HasAdvertised(const Ip6::Prefix &aPrefix) const { return mPrefixes.ContainsMatching(aPrefix); }
uint16_t GetAdvertisedRioCount(void) const { return mPrefixes.GetLength(); }
void HandleTimer(void);
@@ -1041,9 +1067,9 @@
void Add(const Ip6::Prefix &aPrefix);
};
- void SetPreferenceBasedOnRole(void);
- void UpdatePreference(RoutePreference aPreference);
- void AppendRio(const Ip6::Prefix &aPrefix, uint32_t aRouteLifetime, Ip6::Nd::RouterAdvertMessage &aRaMessage);
+ void SetPreferenceBasedOnRole(void);
+ void UpdatePreference(RoutePreference aPreference);
+ Error AppendRio(const Ip6::Prefix &aPrefix, uint32_t aRouteLifetime, RouterAdvert::TxMessage &aRaMessage);
using RioTimer = TimerMilliIn<RoutingManager, &RoutingManager::HandleRioAdvertiserimer>;
@@ -1152,26 +1178,44 @@
struct RaInfo
{
- // Tracks info about emitted RA messages: Number of RAs sent,
- // last tx time, header to use and whether the header is
- // discovered from receiving RAs from the host itself. This
- // ensures that if an entity on host is advertising certain
+ // Tracks info about emitted RA messages:
+ //
+ // - Number of RAs sent
+ // - Last RA TX time
+ // - Hashes of last TX RAs (to tell if a received RA is from
+ // `RoutingManager` itself)
+ // - RA header to use, and
+ // - Whether the RA header is discovered from receiving RAs
+ // from the host itself.
+ //
+ // This ensures that if an entity on host is advertising certain
// info in its RA header (e.g., a default route), the RAs we
// emit from `RoutingManager` also include the same header.
+ typedef Crypto::Sha256::Hash Hash;
+
+ static constexpr uint16_t kNumHashEntries = 5;
+
RaInfo(void)
: mHeaderUpdateTime(TimerMilli::GetNow())
, mIsHeaderFromHost(false)
, mTxCount(0)
, mLastTxTime(TimerMilli::GetNow() - kMinDelayBetweenRtrAdvs)
+ , mLastHashIndex(0)
{
}
- Ip6::Nd::RouterAdvertMessage::Header mHeader;
- TimeMilli mHeaderUpdateTime;
- bool mIsHeaderFromHost;
- uint32_t mTxCount;
- TimeMilli mLastTxTime;
+ void IncrementTxCountAndSaveHash(const InfraIf::Icmp6Packet &aRaMessage);
+ bool IsRaFromManager(const Ip6::Nd::RouterAdvert::RxMessage &aRaMessage) const;
+ static void CalculateHash(const InfraIf::Icmp6Packet &aRaMessage, Hash &aHash);
+
+ RouterAdvert::Header mHeader;
+ TimeMilli mHeaderUpdateTime;
+ bool mIsHeaderFromHost;
+ uint32_t mTxCount;
+ TimeMilli mLastTxTime;
+ Hash mHashes[kNumHashEntries];
+ uint16_t mLastHashIndex;
};
void HandleRsSenderTimer(void) { mRsSender.HandleTimer(); }
@@ -1246,7 +1290,7 @@
}
private:
- Error Process(const Ip6::Nd::RouterAdvertMessage &aMessage);
+ Error Process(const RouterAdvert::RxMessage &aMessage);
void EvaluateStateChange(Dhcp6PdState aOldState);
void WithdrawPrefix(void);
void StartStop(bool aStart);
@@ -1282,17 +1326,16 @@
void HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress);
void HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress);
void HandleNeighborAdvertisement(const InfraIf::Icmp6Packet &aPacket);
- bool ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix);
- bool ShouldProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, const Ip6::Prefix &aPrefix);
+ bool ShouldProcessPrefixInfoOption(const PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix);
+ bool ShouldProcessRouteInfoOption(const RouteInfoOption &aRio, const Ip6::Prefix &aPrefix);
void UpdateDiscoveredPrefixTableOnNetDataChange(void);
bool NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const;
bool NetworkDataContainsUlaRoute(void) const;
- void UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage);
- bool IsReceivedRouterAdvertFromManager(const Ip6::Nd::RouterAdvertMessage &aRaMessage) const;
+ void UpdateRouterAdvertHeader(const RouterAdvert::RxMessage *aRouterAdvertMessage);
void ResetDiscoveredPrefixStaleTimer(void);
static bool IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix);
- static bool IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption &aPio);
+ static bool IsValidOnLinkPrefix(const PrefixInfoOption &aPio);
static bool IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix);
static void LogPrefixInfoOption(const Ip6::Prefix &aPrefix, uint32_t aValidLifetime, uint32_t aPreferredLifetime);
@@ -1334,8 +1377,9 @@
PdPrefixManager mPdPrefixManager;
#endif
- RaInfo mRaInfo;
- RsSender mRsSender;
+ RaInfo mRaInfo;
+ RsSender mRsSender;
+ Heap::Data mExtraRaOptions;
DiscoveredPrefixStaleTimer mDiscoveredPrefixStaleTimer;
RoutingPolicyTimer mRoutingPolicyTimer;
diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp
index 696d0fc..6afd693 100644
--- a/src/core/coap/coap.cpp
+++ b/src/core/coap/coap.cpp
@@ -619,7 +619,6 @@
{
Error error = kErrorNone;
bool isOptionSet = false;
- uint64_t optionBuf = 0;
uint16_t blockOption = 0;
Option::Iterator iterator;
@@ -655,8 +654,9 @@
}
// Copy option
- SuccessOrExit(error = iterator.ReadOptionValue(&optionBuf));
- SuccessOrExit(error = aRequest.AppendOption(optionNumber, iterator.GetOption()->GetLength(), &optionBuf));
+ SuccessOrExit(error = aRequest.AppendOptionFromMessage(optionNumber, iterator.GetOption()->GetLength(),
+ iterator.GetMessage(),
+ iterator.GetOptionValueMessageOffset()));
}
if (!isOptionSet)
diff --git a/src/core/coap/coap_message.cpp b/src/core/coap/coap_message.cpp
index cbf5448..4998de7 100644
--- a/src/core/coap/coap_message.cpp
+++ b/src/core/coap/coap_message.cpp
@@ -146,8 +146,12 @@
return rval;
}
-Error Message::AppendOption(uint16_t aNumber, uint16_t aLength, const void *aValue)
+Error Message::AppendOptionHeader(uint16_t aNumber, uint16_t aLength)
{
+ /*
+ * Appends a CoAP Option header field (Option Delta/Length) per RFC 7252.
+ */
+
Error error = kErrorNone;
uint16_t delta;
uint8_t header[kMaxOptionHeaderSize];
@@ -167,10 +171,33 @@
VerifyOrExit(static_cast<uint32_t>(GetLength()) + headerLength + aLength < kMaxHeaderLength, error = kErrorNoBufs);
SuccessOrExit(error = AppendBytes(header, headerLength));
- SuccessOrExit(error = AppendBytes(aValue, aLength));
GetHelpData().mOptionLast = aNumber;
+exit:
+ return error;
+}
+
+Error Message::AppendOption(uint16_t aNumber, uint16_t aLength, const void *aValue)
+{
+ Error error = kErrorNone;
+
+ SuccessOrExit(error = AppendOptionHeader(aNumber, aLength));
+ SuccessOrExit(error = AppendBytes(aValue, aLength));
+
+ GetHelpData().mHeaderLength = GetLength();
+
+exit:
+ return error;
+}
+
+Error Message::AppendOptionFromMessage(uint16_t aNumber, uint16_t aLength, const Message &aMessage, uint16_t aOffset)
+{
+ Error error = kErrorNone;
+
+ SuccessOrExit(error = AppendOptionHeader(aNumber, aLength));
+ SuccessOrExit(error = AppendBytesFromMessage(aMessage, aOffset, aLength));
+
GetHelpData().mHeaderLength = GetLength();
exit:
diff --git a/src/core/coap/coap_message.hpp b/src/core/coap/coap_message.hpp
index 789c919..62128b6 100644
--- a/src/core/coap/coap_message.hpp
+++ b/src/core/coap/coap_message.hpp
@@ -402,6 +402,23 @@
Error AppendOption(uint16_t aNumber, uint16_t aLength, const void *aValue);
/**
+ * Appends a CoAP option reading Option value from another or potentially the same message.
+ *
+ * @param[in] aNumber The CoAP Option number.
+ * @param[in] aLength The CoAP Option length.
+ * @param[in] aMessage The message to read the CoAP Option value from (it can be the same as the current message).
+ * @param[in] aOffset The offset in @p aMessage to start reading the CoAP Option value from (@p aLength bytes are
+ * used as Option value).
+ *
+ * @retval kErrorNone Successfully appended the option.
+ * @retval kErrorInvalidArgs The option type is not equal or greater than the last option type.
+ * @retval kErrorNoBufs The option length exceeds the buffer size.
+ * @retval kErrorParse Not enough bytes in @p aMessage to read @p aLength bytes from @p aOffset.
+ *
+ */
+ Error AppendOptionFromMessage(uint16_t aNumber, uint16_t aLength, const Message &aMessage, uint16_t aOffset);
+
+ /**
* Appends an unsigned integer CoAP option as specified in RFC-7252 section-3.2
*
* @param[in] aNumber The CoAP Option number.
@@ -966,6 +983,8 @@
}
uint8_t WriteExtendedOptionField(uint16_t aValue, uint8_t *&aBuffer);
+
+ Error AppendOptionHeader(uint16_t aNumber, uint16_t aLength);
};
/**
@@ -1187,6 +1206,16 @@
*/
uint16_t GetPayloadMessageOffset(void) const { return mNextOptionOffset; }
+ /**
+ * Gets the offset of beginning of the CoAP Option Value.
+ *
+ * MUST be used during the iterator is in progress.
+ *
+ * @returns The offset of beginning of the CoAP Option Value
+ *
+ */
+ uint16_t GetOptionValueMessageOffset(void) const { return mNextOptionOffset - mOption.mLength; }
+
private:
// `mOption.mLength` value to indicate iterator is done.
static constexpr uint16_t kIteratorDoneLength = 0xffff;
diff --git a/src/core/common/log.cpp b/src/core/common/log.cpp
index 1809cd5..afddcfd 100644
--- a/src/core/common/log.cpp
+++ b/src/core/common/log.cpp
@@ -136,6 +136,16 @@
return;
}
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
+void Logger::LogOnError(const char *aModuleName, Error aError, const char *aText)
+{
+ if (aError != kErrorNone)
+ {
+ LogAtLevel<kLogLevelWarn>(aModuleName, "Failed to %s: %s", aText, ErrorToString(aError));
+ }
+}
+#endif
+
#if OPENTHREAD_CONFIG_LOG_PKT_DUMP
template <LogLevel kLogLevel>
diff --git a/src/core/common/log.hpp b/src/core/common/log.hpp
index 47293e0..49f0416 100644
--- a/src/core/common/log.hpp
+++ b/src/core/common/log.hpp
@@ -162,6 +162,22 @@
#define LogDebg(...)
#endif
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
+/**
+ * Emits an error log message at warning log level if there is an error.
+ *
+ * The emitted log will use the the following format "Failed to {aText}: {ErrorToString(aError)}", and will be emitted
+ * only if there is an error, i.e., @p aError is not `kErrorNone`.
+ *
+ * @param[in] aError The error to check and log.
+ * @param[in] aText The text to include in the log.
+ *
+ */
+#define LogWarnOnError(aError, aText) Logger::LogOnError(kLogModuleName, aError, aText)
+#else
+#define LogWarnOnError(aError, aText)
+#endif
+
#if OT_SHOULD_LOG
/**
* Emits a log message at a given log level.
@@ -316,6 +332,10 @@
static void LogVarArgs(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, va_list aArgs);
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
+ static void LogOnError(const char *aModuleName, Error aError, const char *aText);
+#endif
+
#if OPENTHREAD_CONFIG_LOG_PKT_DUMP
static constexpr uint8_t kStringLineLength = 80;
static constexpr uint8_t kDumpBytesPerLine = 16;
diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp
index 50e1fcc..3a4c342 100644
--- a/src/core/common/message.cpp
+++ b/src/core/common/message.cpp
@@ -770,12 +770,19 @@
SuccessOrExit(error = messageCopy->AppendBytesFromMessage(*this, 0, aLength));
// Copy selected message information.
+
offset = Min(GetOffset(), aLength);
messageCopy->SetOffset(offset);
messageCopy->SetSubType(GetSubType());
messageCopy->SetLoopbackToHostAllowed(IsLoopbackToHostAllowed());
messageCopy->SetOrigin(GetOrigin());
+ messageCopy->SetTimestamp(GetTimestamp());
+ messageCopy->SetMeshDest(GetMeshDest());
+ messageCopy->SetPanId(GetPanId());
+ messageCopy->SetChannel(GetChannel());
+ messageCopy->SetRssAverager(GetRssAverager());
+ messageCopy->SetLqiAverager(GetLqiAverager());
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
messageCopy->SetTimeSync(IsTimeSync());
#endif
@@ -795,18 +802,48 @@
bool Message::IsChildPending(void) const { return GetMetadata().mChildMask.HasAny(); }
#endif
-void Message::SetLinkInfo(const ThreadLinkInfo &aLinkInfo)
+Error Message::GetLinkInfo(ThreadLinkInfo &aLinkInfo) const
{
- SetLinkSecurityEnabled(aLinkInfo.mLinkSecurity);
- SetPanId(aLinkInfo.mPanId);
- AddRss(aLinkInfo.mRss);
-#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
- AddLqi(aLinkInfo.mLqi);
+ Error error = kErrorNone;
+
+ VerifyOrExit(IsOriginThreadNetif(), error = kErrorNotFound);
+
+ aLinkInfo.Clear();
+
+ aLinkInfo.mPanId = GetPanId();
+ aLinkInfo.mChannel = GetChannel();
+ aLinkInfo.mRss = GetAverageRss();
+ aLinkInfo.mLqi = GetAverageLqi();
+ aLinkInfo.mLinkSecurity = IsLinkSecurityEnabled();
+ aLinkInfo.mIsDstPanIdBroadcast = IsDstPanIdBroadcast();
+
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+ aLinkInfo.mTimeSyncSeq = GetTimeSyncSeq();
+ aLinkInfo.mNetworkTimeOffset = GetNetworkTimeOffset();
#endif
+
+#if OPENTHREAD_CONFIG_MULTI_RADIO
+ aLinkInfo.mRadioType = GetRadioType();
+#endif
+
+exit:
+ return error;
+}
+
+void Message::UpdateLinkInfoFrom(const ThreadLinkInfo &aLinkInfo)
+{
+ SetPanId(aLinkInfo.mPanId);
+ SetChannel(aLinkInfo.mChannel);
+ AddRss(aLinkInfo.mRss);
+ AddLqi(aLinkInfo.mLqi);
+ SetLinkSecurityEnabled(aLinkInfo.mLinkSecurity);
+ GetMetadata().mIsDstPanIdBroadcast = aLinkInfo.IsDstPanIdBroadcast();
+
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
SetTimeSyncSeq(aLinkInfo.mTimeSyncSeq);
SetNetworkTimeOffset(aLinkInfo.mNetworkTimeOffset);
#endif
+
#if OPENTHREAD_CONFIG_MULTI_RADIO
SetRadioType(static_cast<Mac::RadioType>(aLinkInfo.mRadioType));
#endif
diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp
index d48f719..f3dfd54 100644
--- a/src/core/common/message.hpp
+++ b/src/core/common/message.hpp
@@ -200,9 +200,7 @@
uint16_t mPanId; // PAN ID (used for MLE Discover Request and Response).
uint8_t mChannel; // The message channel (used for MLE Announce).
RssAverager mRssAverager; // The averager maintaining the received signal strength (RSS) average.
-#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
- LqiAverager mLqiAverager; // The averager maintaining the Link quality indicator (LQI) average.
-#endif
+ LqiAverager mLqiAverager; // The averager maintaining the Link quality indicator (LQI) average.
#if OPENTHREAD_FTD
ChildMask mChildMask; // ChildMask to indicate which sleepy children need to receive this.
#endif
@@ -218,7 +216,9 @@
bool mMulticastLoop : 1; // Whether this multicast message may be looped back.
bool mResolvingAddress : 1; // Whether the message is pending an address query resolution.
bool mAllowLookbackToHost : 1; // Whether the message is allowed to be looped back to host.
- uint8_t mOrigin : 2; // The origin of the message.
+ bool mIsDstPanIdBroadcast : 1; // IWhether the dest PAN ID is broadcast.
+ uint8_t mOrigin : 2;
+ // The origin of the message.
#if OPENTHREAD_CONFIG_MULTI_RADIO
uint8_t mRadioType : 2; // The radio link type the message was received on, or should be sent on.
bool mIsRadioTypeSet : 1; // Whether the radio type is set.
@@ -1015,11 +1015,15 @@
void SetMeshDest(uint16_t aMeshDest) { GetMetadata().mMeshDest = aMeshDest; }
/**
- * Returns the IEEE 802.15.4 Destination PAN ID.
+ * Returns the IEEE 802.15.4 Source or Destination PAN ID.
*
- * @note Only use this when sending MLE Discover Request or Response messages.
+ * For a message received over the Thread radio, specifies the Source PAN ID when present in MAC header, otherwise
+ * specifies the Destination PAN ID.
*
- * @returns The IEEE 802.15.4 Destination PAN ID.
+ * For a message to be sent over the Thread radio, this is set and used for MLE Discover Request or Response
+ * messages.
+ *
+ * @returns The IEEE 802.15.4 PAN ID.
*
*/
uint16_t GetPanId(void) const { return GetMetadata().mPanId; }
@@ -1035,6 +1039,17 @@
void SetPanId(uint16_t aPanId) { GetMetadata().mPanId = aPanId; }
/**
+ * Indicates whether the Destination PAN ID is broadcast.
+ *
+ * This is applicable for messages received over Thread radio.
+ *
+ * @retval TRUE The Destination PAN ID is broadcast.
+ * @retval FALSE The Destination PAN ID is not broadcast.
+ *
+ */
+ bool IsDstPanIdBroadcast(void) const { return GetMetadata().mIsDstPanIdBroadcast; }
+
+ /**
* Returns the IEEE 802.15.4 Channel to use for transmission.
*
* @note Only use this when sending MLE Announce messages.
@@ -1255,7 +1270,6 @@
*/
const RssAverager &GetRssAverager(void) const { return GetMetadata().mRssAverager; }
-#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
/**
* Updates the average LQI (Link Quality Indicator) associated with the message.
*
@@ -1282,7 +1296,25 @@
*
*/
uint8_t GetPsduCount(void) const { return GetMetadata().mLqiAverager.GetCount(); }
-#endif
+
+ /**
+ * Returns a const reference to LqiAverager of the message.
+ *
+ * @returns A const reference to the LqiAverager of the message.
+ *
+ */
+ const LqiAverager &GetLqiAverager(void) const { return GetMetadata().mLqiAverager; }
+
+ /**
+ * Retrieves `ThreadLinkInfo` from the message if received over Thread radio with origin `kOriginThreadNetif`.
+ *
+ * @pram[out] aLinkInfo A reference to a `ThreadLinkInfo` to populate.
+ *
+ * @retval kErrorNone Successfully retrieved the link info, @p `aLinkInfo` is updated.
+ * @retval kErrorNotFound Message origin is not `kOriginThreadNetif`.
+ *
+ */
+ Error GetLinkInfo(ThreadLinkInfo &aLinkInfo) const;
/**
* Sets the message's link info properties (PAN ID, link security, RSS) from a given `ThreadLinkInfo`.
@@ -1290,7 +1322,7 @@
* @param[in] aLinkInfo The `ThreadLinkInfo` instance from which to set message's related properties.
*
*/
- void SetLinkInfo(const ThreadLinkInfo &aLinkInfo);
+ void UpdateLinkInfoFrom(const ThreadLinkInfo &aLinkInfo);
/**
* Returns a pointer to the message queue (if any) where this message is queued.
@@ -1489,6 +1521,9 @@
void SetMessageQueue(MessageQueue *aMessageQueue);
void SetPriorityQueue(PriorityQueue *aPriorityQueue);
+ void SetRssAverager(const RssAverager &aRssAverager) { GetMetadata().mRssAverager = aRssAverager; }
+ void SetLqiAverager(const LqiAverager &aLqiAverager) { GetMetadata().mLqiAverager = aLqiAverager; }
+
Message *&Next(void) { return GetMetadata().mNext; }
Message *const &Next(void) const { return GetMetadata().mNext; }
Message *&Prev(void) { return GetMetadata().mPrev; }
diff --git a/src/core/common/string.cpp b/src/core/common/string.cpp
index 4019628..c759110 100644
--- a/src/core/common/string.cpp
+++ b/src/core/common/string.cpp
@@ -89,13 +89,16 @@
uint16_t StringLength(const char *aString, uint16_t aMaxLength)
{
- uint16_t ret;
+ uint16_t ret = 0;
- for (ret = 0; (ret < aMaxLength) && (aString[ret] != kNullChar); ret++)
+ VerifyOrExit(aString != nullptr);
+
+ for (; (ret < aMaxLength) && (aString[ret] != kNullChar); ret++)
{
// Empty loop.
}
+exit:
return ret;
}
diff --git a/src/core/common/string.hpp b/src/core/common/string.hpp
index 0c73bb6..358d7f8 100644
--- a/src/core/common/string.hpp
+++ b/src/core/common/string.hpp
@@ -85,8 +85,8 @@
* @param[in] aString A pointer to the string.
* @param[in] aMaxLength The maximum length in bytes.
*
- * @returns The number of characters that precede the terminating null character or @p aMaxLength, whichever is
- * smaller.
+ * @returns The number of characters that precede the terminating null character or @p aMaxLength,
+ * whichever is smaller. `0` if @p aString is `nullptr`.
*
*/
uint16_t StringLength(const char *aString, uint16_t aMaxLength);
diff --git a/src/core/common/tasklet.hpp b/src/core/common/tasklet.hpp
index 48a3f07..5158bdd 100644
--- a/src/core/common/tasklet.hpp
+++ b/src/core/common/tasklet.hpp
@@ -45,8 +45,6 @@
namespace ot {
-class TaskletScheduler;
-
/**
* @addtogroup core-tasklet
*
diff --git a/src/core/config/border_agent.h b/src/core/config/border_agent.h
index 9be5db1..ab3925e 100644
--- a/src/core/config/border_agent.h
+++ b/src/core/config/border_agent.h
@@ -76,6 +76,16 @@
#endif
/**
+ * @def OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+ *
+ * Define to 1 to enable ephemeral key mechanism and its APIs in Border Agent.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+#define OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_4)
+#endif
+
+/**
* @}
*
*/
diff --git a/src/core/config/channel_manager.h b/src/core/config/channel_manager.h
index 7c481c2..4ecb3dc 100644
--- a/src/core/config/channel_manager.h
+++ b/src/core/config/channel_manager.h
@@ -56,6 +56,18 @@
#endif
/**
+ * @def OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE
+ *
+ * Define as 1 to enable Channel Manager support for selecting CSL channels.
+ *
+ * `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE` must be enabled in addition.
+ *
+ */
+#ifndef OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE
+#define OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE 0
+#endif
+
+/**
* @def OPENTHREAD_CONFIG_CHANNEL_MANAGER_MINIMUM_DELAY
*
* The minimum delay (in seconds) used by Channel Manager module for performing a channel change.
diff --git a/src/core/config/misc.h b/src/core/config/misc.h
index b04e32e..70e0c12 100644
--- a/src/core/config/misc.h
+++ b/src/core/config/misc.h
@@ -424,9 +424,14 @@
/**
* @def OPENTHREAD_CONFIG_DEFAULT_SED_BUFFER_SIZE
*
- * This setting configures the default buffer size for IPv6 datagram destined for an attached SED.
- * A Thread Router MUST be able to buffer at least one 1280-octet IPv6 datagram for an attached SED according to
- * the Thread Conformance Specification.
+ * Specifies the value used in emitted Connectivity TLV "Rx-off Child Buffer Size" field which indicates the
+ * guaranteed buffer capacity for all IPv6 datagrams destined to a given rx-off-when-idle child.
+ *
+ * Changing this config does not automatically adjust message buffers. Vendors should ensure their device can support
+ * the specified value based on the message buffer model used:
+ * - OT internal message pool (refer to `OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS` and `MESSAGE_BUFFER_SIZE`), or
+ * - Heap allocated message buffers (refer to `OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE),
+ * - Platform-specific message management (refer to`OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT`).
*
*/
#ifndef OPENTHREAD_CONFIG_DEFAULT_SED_BUFFER_SIZE
@@ -436,9 +441,11 @@
/**
* @def OPENTHREAD_CONFIG_DEFAULT_SED_DATAGRAM_COUNT
*
- * This setting configures the default datagram count of 106-octet IPv6 datagram per attached SED.
- * A Thread Router MUST be able to buffer at least one 106-octet IPv6 datagram per attached SED according to
- * the Thread Conformance Specification.
+ * Specifies the value used in emitted Connectivity TLV "Rx-off Child Datagram Count" field which indicates the
+ * guaranteed queue capacity in number of IPv6 datagrams destined to a given rx-off-when-idle child.
+ *
+ * Similar to `OPENTHREAD_CONFIG_DEFAULT_SED_BUFFER_SIZE`, vendors should ensure their device can support the specified
+ * value based on the message buffer model used.
*
*/
#ifndef OPENTHREAD_CONFIG_DEFAULT_SED_DATAGRAM_COUNT
diff --git a/src/core/config/mle.h b/src/core/config/mle.h
index 0810c01..d09a3cc 100644
--- a/src/core/config/mle.h
+++ b/src/core/config/mle.h
@@ -104,12 +104,12 @@
* Define as 1 to enable feature to set device properties which are used for calculating the local leader weight on a
* device.
*
- * It is enabled by default on Thread Version 1.3.1 or later.
+ * It is enabled by default on Thread Version 1.4 or later.
*
*/
#ifndef OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE
#define OPENTHREAD_CONFIG_MLE_DEVICE_PROPERTY_LEADER_WEIGHT_ENABLE \
- (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_3_1)
+ (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_4)
#endif
/**
diff --git a/src/core/instance/instance.cpp b/src/core/instance/instance.cpp
index 4735ce3..3076110 100644
--- a/src/core/instance/instance.cpp
+++ b/src/core/instance/instance.cpp
@@ -230,7 +230,9 @@
#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
, mChannelMonitor(*this)
#endif
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+ (OPENTHREAD_FTD || \
+ (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
, mChannelManager(*this)
#endif
#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD
diff --git a/src/core/instance/instance.hpp b/src/core/instance/instance.hpp
index 1ebc01c..30b443b 100644
--- a/src/core/instance/instance.hpp
+++ b/src/core/instance/instance.hpp
@@ -645,7 +645,9 @@
Utils::ChannelMonitor mChannelMonitor;
#endif
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+ (OPENTHREAD_FTD || \
+ (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
Utils::ChannelManager mChannelManager;
#endif
@@ -946,7 +948,9 @@
template <> inline Utils::ChannelMonitor &Instance::Get(void) { return mChannelMonitor; }
#endif
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+ (OPENTHREAD_FTD || \
+ (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
template <> inline Utils::ChannelManager &Instance::Get(void) { return mChannelManager; }
#endif
diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp
index 243db1d..e70fc98 100644
--- a/src/core/mac/mac.cpp
+++ b/src/core/mac/mac.cpp
@@ -1134,9 +1134,13 @@
}
// Only track the CCA success rate for frame transmissions
- // on the PAN channel.
+ // on the PAN channel or the CSL channel.
+#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
+ if ((aChannel == mPanChannel) || (IsCslEnabled() && (aChannel == mCslChannel)))
+#else
if (aChannel == mPanChannel)
+#endif
{
if (mCcaSampleCount < kMaxCcaSampleCount)
{
@@ -1637,7 +1641,7 @@
if (keySequence > keyManager.GetCurrentKeySequence())
{
- keyManager.SetCurrentKeySequence(keySequence);
+ keyManager.SetCurrentKeySequence(keySequence, KeyManager::kApplyKeySwitchGuard);
}
}
diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp
index 3e84465..9946bb2 100644
--- a/src/core/mac/mac_frame.cpp
+++ b/src/core/mac/mac_frame.cpp
@@ -1220,9 +1220,11 @@
{
uint8_t *cur = GetThreadIe(ThreadIe::kEnhAckProbingIe);
- OT_ASSERT(cur != nullptr);
-
+ VerifyOrExit(cur != nullptr);
memcpy(cur + sizeof(HeaderIe) + sizeof(VendorIeHeader), aValue, aLen);
+
+exit:
+ return;
}
#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp
index 142c606..24e7331 100644
--- a/src/core/meshcop/border_agent.cpp
+++ b/src/core/meshcop/border_agent.cpp
@@ -128,7 +128,7 @@
exit:
FreeMessageOnError(message, error);
- LogError("send error CoAP message", error);
+ LogWarnOnError(error, "send error CoAP message");
}
void BorderAgent::SendErrorMessage(const Coap::Message &aRequest, bool aSeparate, Error aError)
@@ -158,7 +158,7 @@
exit:
FreeMessageOnError(message, error);
- LogError("send error CoAP message", error);
+ LogWarnOnError(error, "send error CoAP message");
}
Error BorderAgent::SendMessage(Coap::Message &aMessage)
@@ -239,6 +239,12 @@
#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
, mIdInitialized(false)
#endif
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+ , mUsingEphemeralKey(false)
+ , mOldUdpPort(0)
+ , mEphemeralKeyTimer(aInstance)
+ , mEphemeralKeyTask(aInstance)
+#endif
{
mCommissionerAloc.InitAsThreadOriginMeshLocal();
}
@@ -341,7 +347,7 @@
exit:
FreeMessageOnError(message, error);
- LogError("send proxy stream", error);
+ LogWarnOnError(error, "send proxy stream");
}
bool BorderAgent::HandleUdpReceive(void *aContext, const otMessage *aMessage, const otMessageInfo *aMessageInfo)
@@ -392,7 +398,7 @@
FreeMessageOnError(message, error);
if (error != kErrorDestinationAddressFiltered)
{
- LogError("notify commissioner on ProxyRx (c/ur)", error);
+ LogWarnOnError(error, "notify commissioner on ProxyRx (c/ur)");
}
return error != kErrorDestinationAddressFiltered;
@@ -430,7 +436,7 @@
LogInfo("Sent to commissioner");
exit:
- LogError("send to commissioner", error);
+ LogWarnOnError(error, "send to commissioner");
return error;
}
@@ -514,7 +520,7 @@
exit:
FreeMessageOnError(message, error);
- LogError("send to joiner router request RelayTx (c/tx)", error);
+ LogWarnOnError(error, "send to joiner router request RelayTx (c/tx)");
}
Error BorderAgent::ForwardToLeader(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Uri aUri)
@@ -570,7 +576,7 @@
LogInfo("Forwarded request to leader on %s", PathForUri(aUri));
exit:
- LogError("forward to leader", error);
+ LogWarnOnError(error, "forward to leader");
if (error != kErrorNone)
{
@@ -599,25 +605,55 @@
LogInfo("Commissioner disconnected");
IgnoreError(Get<Ip6::Udp>().RemoveReceiver(mUdpReceiver));
Get<ThreadNetif>().RemoveUnicastAddress(mCommissionerAloc);
- mState = kStateStarted;
- mUdpProxyPort = 0;
+
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+ if (mUsingEphemeralKey)
+ {
+ RestartAfterRemovingEphemeralKey();
+ }
+ else
+#endif
+ {
+ mState = kStateStarted;
+ mUdpProxyPort = 0;
+ }
}
}
uint16_t BorderAgent::GetUdpPort(void) const { return Get<Tmf::SecureAgent>().GetUdpPort(); }
-void BorderAgent::Start(void)
+Error BorderAgent::Start(uint16_t aUdpPort)
{
Error error;
Pskc pskc;
- VerifyOrExit(mState == kStateStopped, error = kErrorNone);
-
Get<KeyManager>().GetPskc(pskc);
- SuccessOrExit(error = Get<Tmf::SecureAgent>().Start(kUdpPort));
- SuccessOrExit(error = Get<Tmf::SecureAgent>().SetPsk(pskc.m8, Pskc::kSize));
-
+ error = Start(aUdpPort, pskc.m8, Pskc::kSize);
pskc.Clear();
+
+ return error;
+}
+
+Error BorderAgent::Start(uint16_t aUdpPort, const uint8_t *aPsk, uint8_t aPskLength)
+{
+ Error error = kErrorNone;
+
+ VerifyOrExit(mState == kStateStopped);
+
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+ if (mUsingEphemeralKey)
+ {
+ SuccessOrExit(error = Get<Tmf::SecureAgent>().Start(aUdpPort, kMaxEphemeralKeyConnectionAttempts,
+ HandleSecureAgentStopped, this));
+ }
+ else
+#endif
+ {
+ SuccessOrExit(error = Get<Tmf::SecureAgent>().Start(aUdpPort));
+ }
+
+ SuccessOrExit(error = Get<Tmf::SecureAgent>().SetPsk(aPsk, aPskLength));
+
Get<Tmf::SecureAgent>().SetConnectedCallback(HandleConnected, this);
mState = kStateStarted;
@@ -626,7 +662,8 @@
LogInfo("Border Agent start listening on port %u", GetUdpPort());
exit:
- LogError("start agent", error);
+ LogWarnOnError(error, "start agent");
+ return error;
}
void BorderAgent::HandleTimeout(void)
@@ -642,27 +679,128 @@
{
VerifyOrExit(mState != kStateStopped);
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+ if (mUsingEphemeralKey)
+ {
+ mUsingEphemeralKey = false;
+ mEphemeralKeyTimer.Stop();
+ mEphemeralKeyTask.Post();
+ }
+#endif
+
mTimer.Stop();
Get<Tmf::SecureAgent>().Stop();
mState = kStateStopped;
mUdpProxyPort = 0;
-
LogInfo("Border Agent stopped");
exit:
return;
}
-#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
-void BorderAgent::LogError(const char *aActionText, Error aError)
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+
+Error BorderAgent::SetEphemeralKey(const char *aKeyString, uint32_t aTimeout, uint16_t aUdpPort)
{
- if (aError != kErrorNone)
+ Error error = kErrorNone;
+ uint16_t length = StringLength(aKeyString, kMaxEphemeralKeyLength + 1);
+
+ VerifyOrExit(mState == kStateStarted, error = kErrorInvalidState);
+ VerifyOrExit((length >= kMinEphemeralKeyLength) && (length <= kMaxEphemeralKeyLength), error = kErrorInvalidArgs);
+
+ if (!mUsingEphemeralKey)
{
- LogWarn("Failed to %s: %s", aActionText, ErrorToString(aError));
+ mOldUdpPort = GetUdpPort();
}
+
+ Stop();
+
+ // We set the `mUsingEphemeralKey` before `Start()` since
+ // callbacks (like `HandleConnected()`) may be invoked from
+ // `Start()` itself.
+
+ mUsingEphemeralKey = true;
+
+ error = Start(aUdpPort, reinterpret_cast<const uint8_t *>(aKeyString), static_cast<uint8_t>(length));
+
+ if (error != kErrorNone)
+ {
+ mUsingEphemeralKey = false;
+ IgnoreError(Start(mOldUdpPort));
+ ExitNow();
+ }
+
+ mEphemeralKeyTask.Post();
+
+ if (aTimeout == 0)
+ {
+ aTimeout = kDefaultEphemeralKeyTimeout;
+ }
+
+ aTimeout = Min(aTimeout, kMaxEphemeralKeyTimeout);
+
+ mEphemeralKeyTimer.Start(aTimeout);
+
+ LogInfo("Allow ephemeral key for %lu msec on port %u", ToUlong(aTimeout), GetUdpPort());
+
+exit:
+ return error;
}
-#endif
+
+void BorderAgent::ClearEphemeralKey(void)
+{
+ VerifyOrExit(mUsingEphemeralKey);
+
+ LogInfo("Clearing ephemeral key");
+ mEphemeralKeyTimer.Stop();
+
+ switch (mState)
+ {
+ case kStateStarted:
+ RestartAfterRemovingEphemeralKey();
+ break;
+
+ case kStateStopped:
+ case kStateActive:
+ // If there is an active commissioner connection, we wait till
+ // it gets disconnected before removing ephemeral key and
+ // restarting the agent.
+ break;
+ }
+
+exit:
+ return;
+}
+
+void BorderAgent::HandleEphemeralKeyTimeout(void)
+{
+ LogInfo("Ephemeral key timed out");
+ ClearEphemeralKey();
+}
+
+void BorderAgent::InvokeEphemeralKeyCallback(void) { mEphemeralKeyCallback.InvokeIfSet(); }
+
+void BorderAgent::RestartAfterRemovingEphemeralKey(void)
+{
+ LogInfo("Removing ephemeral key and restarting agent");
+
+ Stop();
+ IgnoreError(Start(mOldUdpPort));
+}
+
+void BorderAgent::HandleSecureAgentStopped(void *aContext)
+{
+ reinterpret_cast<BorderAgent *>(aContext)->HandleSecureAgentStopped();
+}
+
+void BorderAgent::HandleSecureAgentStopped(void)
+{
+ LogInfo("Reached max allowed connection attempts with ephemeral key");
+ RestartAfterRemovingEphemeralKey();
+}
+
+#endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
} // namespace MeshCoP
} // namespace ot
diff --git a/src/core/meshcop/border_agent.hpp b/src/core/meshcop/border_agent.hpp
index 9f721d6..933a95c 100644
--- a/src/core/meshcop/border_agent.hpp
+++ b/src/core/meshcop/border_agent.hpp
@@ -45,6 +45,8 @@
#include "common/locator.hpp"
#include "common/non_copyable.hpp"
#include "common/notifier.hpp"
+#include "common/tasklet.hpp"
+#include "meshcop/secure_transport.hpp"
#include "net/udp6.hpp"
#include "thread/tmf.hpp"
#include "thread/uri_paths.hpp"
@@ -60,6 +62,30 @@
friend class Tmf::SecureAgent;
public:
+ /**
+ * Minimum length of the ephemeral key string.
+ *
+ */
+ static constexpr uint16_t kMinEphemeralKeyLength = OT_BORDER_AGENT_MIN_EPHEMERAL_KEY_LENGTH;
+
+ /**
+ * Maximum length of the ephemeral key string.
+ *
+ */
+ static constexpr uint16_t kMaxEphemeralKeyLength = OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_LENGTH;
+
+ /**
+ * Default ephemeral key timeout interval in milliseconds.
+ *
+ */
+ static constexpr uint32_t kDefaultEphemeralKeyTimeout = OT_BORDER_AGENT_DEFAULT_EPHEMERAL_KEY_TIMEOUT;
+
+ /**
+ * Maximum ephemeral key timeout interval in milliseconds.
+ *
+ */
+ static constexpr uint32_t kMaxEphemeralKeyTimeout = OT_BORDER_AGENT_MAX_EPHEMERAL_KEY_TIMEOUT;
+
typedef otBorderAgentId Id; ///< Border Agent ID.
/**
@@ -125,7 +151,7 @@
* Starts the Border Agent service.
*
*/
- void Start(void);
+ void Start(void) { IgnoreError(Start(kUdpPort)); }
/**
* Stops the Border Agent service.
@@ -141,6 +167,75 @@
*/
State GetState(void) const { return mState; }
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+ /**
+ * Sets the ephemeral key for a given timeout duration.
+ *
+ * The ephemeral key can be set when the Border Agent is already running and is not currently connected to any
+ * external commissioner (i.e., it is in `kStateStarted` state).
+ *
+ * The given @p aKeyString is directly used as the ephemeral PSK (excluding the trailing null `\0` character). Its
+ * length must be between `kMinEphemeralKeyLength` and `kMaxEphemeralKeyLength`, inclusive.
+ *
+ * Setting the ephemeral key again before a previously set one is timed out will replace the previous one and will
+ * reset the timeout.
+ *
+ * While the timeout interval is in effect, the ephemeral key can be used only once by an external commissioner to
+ * connect. Once the commissioner disconnects, the ephemeral key is cleared, and Border Agent reverts to using
+ * PSKc.
+ *
+ * @param[in] aKeyString The ephemeral key.
+ * @param[in] aTimeout The timeout duration in milliseconds to use the ephemeral key.
+ * If zero, the default `kDefaultEphemeralKeyTimeout` value will be used.
+ * If the timeout value is larger than `kMaxEphemeralKeyTimeout`, the max value will be
+ * used instead.
+ * @param[in] aUdpPort The UDP port to use with ephemeral key. If UDP port is zero, an ephemeral port will be
+ * used. `GetUdpPort()` will return the current UDP port being used.
+ *
+ * @retval kErrorNone Successfully set the ephemeral key.
+ * @retval kErrorInvalidState Agent is not running or connected to external commissioner.
+ * @retval kErrorInvalidArgs The given @p aKeyString is not valid.
+ * @retval kErrorFailed Failed to set the key (e.g., could not bind to UDP port).
+ *
+ */
+ Error SetEphemeralKey(const char *aKeyString, uint32_t aTimeout, uint16_t aUdpPort);
+
+ /**
+ * Cancels the ephemeral key in use if any.
+ *
+ * Can be used to cancel a previously set ephemeral key before it times out. If the Border Agent is not running or
+ * there is no ephemeral key in use, calling this function has no effect.
+ *
+ * If a commissioner is connected using the ephemeral key and is currently active, calling this method does not
+ * change its state. In this case the `IsEphemeralKeyActive()` will continue to return `true` until the commissioner
+ * disconnects.
+ *
+ */
+ void ClearEphemeralKey(void);
+
+ /**
+ * Indicates whether or not an ephemeral key is currently active.
+ *
+ * @retval TRUE An ephemeral key is active.
+ * @retval FALSE No ephemeral key is active.
+ *
+ */
+ bool IsEphemeralKeyActive(void) const { return mUsingEphemeralKey; }
+
+ /**
+ * Callback function pointer to notify when there is any changes related to use of ephemeral key by Border Agent.
+ *
+ *
+ */
+ typedef otBorderAgentEphemeralKeyCallback EphemeralKeyCallback;
+
+ void SetEphemeralKeyCallback(EphemeralKeyCallback aCallback, void *aContext)
+ {
+ mEphemeralKeyCallback.Set(aCallback, aContext);
+ }
+
+#endif // OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+
/**
* Returns the UDP Proxy port to which the commissioner is currently
* bound.
@@ -151,9 +246,16 @@
uint16_t GetUdpProxyPort(void) const { return mUdpProxyPort; }
private:
+ static_assert(kMaxEphemeralKeyLength <= SecureTransport::kPskMaxLength,
+ "Max ephemeral key length is larger than max PSK len");
+
static constexpr uint16_t kUdpPort = OPENTHREAD_CONFIG_BORDER_AGENT_UDP_PORT;
static constexpr uint32_t kKeepAliveTimeout = 50 * 1000; // Timeout to reject a commissioner (in msec)
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+ static constexpr uint16_t kMaxEphemeralKeyConnectionAttempts = 10;
+#endif
+
class ForwardContext : public InstanceLocatorInit, public Heap::Allocatable<ForwardContext>
{
public:
@@ -171,6 +273,9 @@
uint8_t mToken[Coap::Message::kMaxTokenLength]; // The CoAP Token of the original request.
};
+ Error Start(uint16_t aUdpPort);
+ Error Start(uint16_t aUdpPort, const uint8_t *aPsk, uint8_t aPskLength);
+
void HandleNotifierEvents(Events aEvents);
Coap::Message::Code CoapCodeFromError(Error aError);
@@ -185,6 +290,14 @@
void HandleTimeout(void);
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+ void RestartAfterRemovingEphemeralKey(void);
+ void HandleEphemeralKeyTimeout(void);
+ void InvokeEphemeralKeyCallback(void);
+ static void HandleSecureAgentStopped(void *aContext);
+ void HandleSecureAgentStopped(void);
+#endif
+
static void HandleCoapResponse(void *aContext,
otMessage *aMessage,
const otMessageInfo *aMessageInfo,
@@ -195,13 +308,11 @@
static bool HandleUdpReceive(void *aContext, const otMessage *aMessage, const otMessageInfo *aMessageInfo);
bool HandleUdpReceive(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
-#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
- void LogError(const char *aActionText, Error aError);
-#else
- void LogError(const char *, Error) {}
-#endif
-
using TimeoutTimer = TimerMilliIn<BorderAgent, &BorderAgent::HandleTimeout>;
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+ using EphemeralKeyTimer = TimerMilliIn<BorderAgent, &BorderAgent::HandleEphemeralKeyTimeout>;
+ using EphemeralKeyTask = TaskletIn<BorderAgent, &BorderAgent::InvokeEphemeralKeyCallback>;
+#endif
State mState;
uint16_t mUdpProxyPort;
@@ -212,6 +323,13 @@
Id mId;
bool mIdInitialized;
#endif
+#if OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE
+ bool mUsingEphemeralKey;
+ uint16_t mOldUdpPort;
+ EphemeralKeyTimer mEphemeralKeyTimer;
+ EphemeralKeyTask mEphemeralKeyTask;
+ Callback<EphemeralKeyCallback> mEphemeralKeyCallback;
+#endif
};
DeclareTmfHandler(BorderAgent, kUriRelayRx);
diff --git a/src/core/meshcop/commissioner.cpp b/src/core/meshcop/commissioner.cpp
index 92a52c1..999d732 100644
--- a/src/core/meshcop/commissioner.cpp
+++ b/src/core/meshcop/commissioner.cpp
@@ -302,9 +302,9 @@
if ((error != kErrorNone) && (error != kErrorAlready))
{
Get<Tmf::SecureAgent>().Stop();
+ LogWarnOnError(error, "start commissioner");
}
- LogError("start commissioner", error);
return error;
}
@@ -343,7 +343,11 @@
#endif
exit:
- LogError("stop commissioner", error);
+ if (error != kErrorAlready)
+ {
+ LogWarnOnError(error, "stop commissioner");
+ }
+
return error;
}
@@ -405,7 +409,8 @@
error = SendMgmtCommissionerSetRequest(dataset, nullptr, 0);
exit:
- LogError("send MGMT_COMMISSIONER_SET.req", error);
+ LogWarnOnError(error, "send MGMT_COMMISSIONER_SET.req");
+ OT_UNUSED_VARIABLE(error);
}
void Commissioner::ClearJoiners(void)
@@ -857,7 +862,7 @@
exit:
FreeMessageOnError(message, error);
- LogError("send keep alive", error);
+ LogWarnOnError(error, "send keep alive");
}
void Commissioner::HandleLeaderKeepAliveResponse(void *aContext,
diff --git a/src/core/meshcop/dataset.cpp b/src/core/meshcop/dataset.cpp
index 15c547b..0285956 100644
--- a/src/core/meshcop/dataset.cpp
+++ b/src/core/meshcop/dataset.cpp
@@ -251,10 +251,10 @@
}
}
-void Dataset::ConvertTo(otOperationalDatasetTlvs &aDataset) const
+void Dataset::ConvertTo(Tlvs &aTlvs) const
{
- memcpy(aDataset.mTlvs, mTlvs, mLength);
- aDataset.mLength = static_cast<uint8_t>(mLength);
+ memcpy(aTlvs.mTlvs, mTlvs, mLength);
+ aTlvs.mLength = static_cast<uint8_t>(mLength);
}
void Dataset::Set(Type aType, const Dataset &aDataset)
@@ -271,10 +271,10 @@
mUpdateTime = aDataset.GetUpdateTime();
}
-void Dataset::SetFrom(const otOperationalDatasetTlvs &aDataset)
+void Dataset::SetFrom(const Tlvs &aTlvs)
{
- mLength = aDataset.mLength;
- memcpy(mTlvs, aDataset.mTlvs, mLength);
+ mLength = aTlvs.mLength;
+ memcpy(mTlvs, aTlvs.mTlvs, mLength);
}
Error Dataset::SetFrom(const Info &aDatasetInfo)
diff --git a/src/core/meshcop/dataset.hpp b/src/core/meshcop/dataset.hpp
index 131a271..5fbc799 100644
--- a/src/core/meshcop/dataset.hpp
+++ b/src/core/meshcop/dataset.hpp
@@ -76,6 +76,12 @@
};
/**
+ * Represents a Dataset as a sequence of TLVs.
+ *
+ */
+ typedef otOperationalDatasetTlvs Tlvs;
+
+ /**
* Represents presence of different components in Active or Pending Operational Dataset.
*
*/
@@ -770,10 +776,10 @@
/**
* Converts the TLV representation to structure representation.
*
- * @param[out] aDataset A reference to `otOperationalDatasetTlvs` to output the Dataset.
+ * @param[out] aTlvs A reference to output the Dataset as a sequence of TLVs.
*
*/
- void ConvertTo(otOperationalDatasetTlvs &aDataset) const;
+ void ConvertTo(Tlvs &aTlvs) const;
/**
* Returns the Dataset size in bytes.
@@ -859,10 +865,10 @@
/**
* Sets the Dataset using @p aDataset.
*
- * @param[in] aDataset The input Dataset as otOperationalDatasetTlvs.
+ * @param[in] aDataset The input Dataset as `Tlvs`.
*
*/
- void SetFrom(const otOperationalDatasetTlvs &aDataset);
+ void SetFrom(const Tlvs &aTlvs);
/**
* Appends the MLE Dataset TLV but excluding MeshCoP Sub Timestamp TLV.
diff --git a/src/core/meshcop/dataset_local.cpp b/src/core/meshcop/dataset_local.cpp
index edb8108..ba4c5fc 100644
--- a/src/core/meshcop/dataset_local.cpp
+++ b/src/core/meshcop/dataset_local.cpp
@@ -147,15 +147,15 @@
return error;
}
-Error DatasetLocal::Read(otOperationalDatasetTlvs &aDataset) const
+Error DatasetLocal::Read(Dataset::Tlvs &aDatasetTlvs) const
{
Dataset dataset;
Error error;
- ClearAllBytes(aDataset);
+ ClearAllBytes(aDatasetTlvs);
SuccessOrExit(error = Read(dataset));
- dataset.ConvertTo(aDataset);
+ dataset.ConvertTo(aDatasetTlvs);
exit:
return error;
@@ -173,11 +173,11 @@
return error;
}
-Error DatasetLocal::Save(const otOperationalDatasetTlvs &aDataset)
+Error DatasetLocal::Save(const Dataset::Tlvs &aDatasetTlvs)
{
Dataset dataset;
- dataset.SetFrom(aDataset);
+ dataset.SetFrom(aDatasetTlvs);
return Save(dataset);
}
diff --git a/src/core/meshcop/dataset_local.hpp b/src/core/meshcop/dataset_local.hpp
index 249f069..40e52bd 100644
--- a/src/core/meshcop/dataset_local.hpp
+++ b/src/core/meshcop/dataset_local.hpp
@@ -134,13 +134,13 @@
/**
* Retrieves the dataset from non-volatile memory.
*
- * @param[out] aDataset Where to place the dataset.
+ * @param[out] aDatasetTlvs Where to place the dataset.
*
* @retval kErrorNone Successfully retrieved the dataset.
* @retval kErrorNotFound There is no corresponding dataset stored in non-volatile memory.
*
*/
- Error Read(otOperationalDatasetTlvs &aDataset) const;
+ Error Read(Dataset::Tlvs &aDatasetTlvs) const;
/**
* Returns the local time this dataset was last updated or restored.
@@ -164,13 +164,13 @@
/**
* Stores the dataset into non-volatile memory.
*
- * @param[in] aDataset The Dataset to save as `otOperationalDatasetTlvs`.
+ * @param[in] aDatasetTlvs The Dataset to save as `Dataset::Tlvs`.
*
* @retval kErrorNone Successfully saved the dataset.
* @retval kErrorNotImplemented The platform does not implement settings functionality.
*
*/
- Error Save(const otOperationalDatasetTlvs &aDataset);
+ Error Save(const Dataset::Tlvs &aDatasetTlvs);
/**
* Stores the dataset into non-volatile memory.
diff --git a/src/core/meshcop/dataset_manager.cpp b/src/core/meshcop/dataset_manager.cpp
index 8bf1cb3..6c49a23 100644
--- a/src/core/meshcop/dataset_manager.cpp
+++ b/src/core/meshcop/dataset_manager.cpp
@@ -160,11 +160,11 @@
return error;
}
-Error DatasetManager::Save(const otOperationalDatasetTlvs &aDataset)
+Error DatasetManager::Save(const Dataset::Tlvs &aDatasetTlvs)
{
Error error;
- SuccessOrExit(error = mLocal.Save(aDataset));
+ SuccessOrExit(error = mLocal.Save(aDatasetTlvs));
HandleDatasetUpdated();
exit:
@@ -292,7 +292,11 @@
OT_FALL_THROUGH;
default:
- LogError("send Dataset set to leader", error);
+ if (error != kErrorAlready)
+ {
+ LogWarnOnError(error, "send Dataset set to leader");
+ }
+
FreeMessage(message);
break;
}
@@ -697,11 +701,11 @@
return error;
}
-Error PendingDatasetManager::Save(const otOperationalDatasetTlvs &aDataset)
+Error PendingDatasetManager::Save(const Dataset::Tlvs &aDatasetTlvs)
{
Error error;
- SuccessOrExit(error = DatasetManager::Save(aDataset));
+ SuccessOrExit(error = DatasetManager::Save(aDatasetTlvs));
StartDelayTimer();
exit:
diff --git a/src/core/meshcop/dataset_manager.hpp b/src/core/meshcop/dataset_manager.hpp
index 36162a3..c0d90fe 100644
--- a/src/core/meshcop/dataset_manager.hpp
+++ b/src/core/meshcop/dataset_manager.hpp
@@ -96,13 +96,13 @@
/**
* Retrieves the dataset from non-volatile memory.
*
- * @param[out] aDataset Where to place the dataset.
+ * @param[out] aDatasetTlvs Where to place the dataset.
*
* @retval kErrorNone Successfully retrieved the dataset.
* @retval kErrorNotFound There is no corresponding dataset stored in non-volatile memory.
*
*/
- Error Read(otOperationalDatasetTlvs &aDataset) const { return mLocal.Read(aDataset); }
+ Error Read(Dataset::Tlvs &aDatasetTlvs) const { return mLocal.Read(aDatasetTlvs); }
/**
* Retrieves the channel mask from local dataset.
@@ -261,13 +261,13 @@
/**
* Saves the Operational Dataset in non-volatile memory.
*
- * @param[in] aDataset The Operational Dataset.
+ * @param[in] aDatasetTlvs The Operational Dataset as `Dataset::Tlvs`.
*
* @retval kErrorNone Successfully saved the dataset.
* @retval kErrorNotImplemented The platform does not implement settings functionality.
*
*/
- Error Save(const otOperationalDatasetTlvs &aDataset);
+ Error Save(const Dataset::Tlvs &aDatasetTlvs);
/**
* Sets the Operational Dataset for the partition.
@@ -459,13 +459,13 @@
/**
* Sets the Operational Dataset in non-volatile memory.
*
- * @param[in] aDataset The Operational Dataset.
+ * @param[in] aDatasetTlvs The Operational Dataset as `Dataset::Tlvs`.
*
* @retval kErrorNone Successfully saved the dataset.
* @retval kErrorNotImplemented The platform does not implement settings functionality.
*
*/
- Error Save(const otOperationalDatasetTlvs &aDataset) { return DatasetManager::Save(aDataset); }
+ Error Save(const Dataset::Tlvs &aDatasetTlvs) { return DatasetManager::Save(aDatasetTlvs); }
#if OPENTHREAD_FTD
@@ -558,13 +558,13 @@
*
* Also starts the Delay Timer.
*
- * @param[in] aDataset The Operational Dataset.
+ * @param[in] aDatasetTlvs The Operational Dataset as a sequence of TLVs.
*
* @retval kErrorNone Successfully saved the dataset.
* @retval kErrorNotImplemented The platform does not implement settings functionality.
*
*/
- Error Save(const otOperationalDatasetTlvs &aDataset);
+ Error Save(const Dataset::Tlvs &aDatasetTlvs);
/**
* Sets the Operational Dataset for the partition.
diff --git a/src/core/meshcop/joiner.cpp b/src/core/meshcop/joiner.cpp
index fb0f3e6..4df5f96 100644
--- a/src/core/meshcop/joiner.cpp
+++ b/src/core/meshcop/joiner.cpp
@@ -185,7 +185,7 @@
FreeJoinerFinalizeMessage();
}
- LogError("start joiner", error);
+ LogWarnOnError(error, "start joiner");
return error;
}
@@ -378,7 +378,7 @@
SetState(kStateConnect);
exit:
- LogError("start secure joiner connection", error);
+ LogWarnOnError(error, "start secure joiner connection");
return error;
}
@@ -543,7 +543,7 @@
mTimer.Start(kConfigExtAddressDelay);
exit:
- LogError("process joiner entrust", error);
+ LogWarnOnError(error, "process joiner entrust");
}
void Joiner::SendJoinerEntrustResponse(const Coap::Message &aRequest, const Ip6::MessageInfo &aRequestInfo)
diff --git a/src/core/meshcop/joiner_router.cpp b/src/core/meshcop/joiner_router.cpp
index acadf1a..a5df413 100644
--- a/src/core/meshcop/joiner_router.cpp
+++ b/src/core/meshcop/joiner_router.cpp
@@ -232,7 +232,7 @@
exit:
FreeMessageOnError(message, error);
- LogError("schedule joiner entrust", error);
+ LogWarnOnError(error, "schedule joiner entrust");
}
void JoinerRouter::HandleTimer(void) { SendDelayedJoinerEntrust(); }
diff --git a/src/core/meshcop/meshcop.cpp b/src/core/meshcop/meshcop.cpp
index b30fe27..8bce745 100644
--- a/src/core/meshcop/meshcop.cpp
+++ b/src/core/meshcop/meshcop.cpp
@@ -343,15 +343,5 @@
}
#endif // OPENTHREAD_FTD
-#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
-void LogError(const char *aActionText, Error aError)
-{
- if (aError != kErrorNone && aError != kErrorAlready)
- {
- LogWarn("Failed to %s: %s", aActionText, ErrorToString(aError));
- }
-}
-#endif
-
} // namespace MeshCoP
} // namespace ot
diff --git a/src/core/meshcop/meshcop.hpp b/src/core/meshcop/meshcop.hpp
index 339872b..3620927 100644
--- a/src/core/meshcop/meshcop.hpp
+++ b/src/core/meshcop/meshcop.hpp
@@ -561,22 +561,6 @@
*/
void ComputeJoinerId(const Mac::ExtAddress &aEui64, Mac::ExtAddress &aJoinerId);
-#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
-/**
- * Emits a log message indicating an error during a MeshCoP action.
- *
- * Note that log message is emitted only if there is an error, i.e. @p aError is not `kErrorNone`. The log
- * message will have the format "Failed to {aActionText} : {ErrorString}".
- *
- * @param[in] aActionText A string representing the failed action.
- * @param[in] aError The error in sending the message.
- *
- */
-void LogError(const char *aActionText, Error aError);
-#else
-inline void LogError(const char *, Error) {}
-#endif
-
} // namespace MeshCoP
DefineCoreType(otJoinerPskd, MeshCoP::JoinerPskd);
diff --git a/src/core/meshcop/meshcop_leader.cpp b/src/core/meshcop/meshcop_leader.cpp
index 64c028d..4fc8385 100644
--- a/src/core/meshcop/meshcop_leader.cpp
+++ b/src/core/meshcop/meshcop_leader.cpp
@@ -125,7 +125,7 @@
exit:
FreeMessageOnError(message, error);
- LogError("send petition response", error);
+ LogWarnOnError(error, "send petition response");
}
template <> void Leader::HandleTmf<kUriLeaderKeepAlive>(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
@@ -192,7 +192,7 @@
exit:
FreeMessageOnError(message, error);
- LogError("send keep alive response", error);
+ LogWarnOnError(error, "send keep alive response");
}
void Leader::SendDatasetChanged(const Ip6::Address &aAddress)
@@ -211,7 +211,7 @@
exit:
FreeMessageOnError(message, error);
- LogError("send dataset changed", error);
+ LogWarnOnError(error, "send dataset changed");
}
Error Leader::SetDelayTimerMinimal(uint32_t aDelayTimerMinimal)
diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp
index f8df773..f8e4596 100644
--- a/src/core/meshcop/tcat_agent.cpp
+++ b/src/core/meshcop/tcat_agent.cpp
@@ -94,7 +94,7 @@
mAlreadyCommissioned = false;
exit:
- LogError("start TCAT agent", error);
+ LogWarnOnError(error, "start TCAT agent");
return error;
}
@@ -461,9 +461,9 @@
Error TcatAgent::HandleSetActiveOperationalDataset(const Message &aIncommingMessage, uint16_t aOffset, uint16_t aLength)
{
- Dataset dataset;
- otOperationalDatasetTlvs datasetTlvs;
- Error error;
+ Dataset dataset;
+ Dataset::Tlvs datasetTlvs;
+ Error error;
SuccessOrExit(error = dataset.ReadFromMessage(aIncommingMessage, aOffset, aLength));
@@ -500,16 +500,6 @@
return error;
}
-#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
-void TcatAgent::LogError(const char *aActionText, Error aError)
-{
- if (aError != kErrorNone)
- {
- LogWarn("Failed to %s: %s", aActionText, ErrorToString(aError));
- }
-}
-#endif
-
} // namespace MeshCoP
} // namespace ot
diff --git a/src/core/meshcop/tcat_agent.hpp b/src/core/meshcop/tcat_agent.hpp
index d2dd9f3..16cf8f9 100644
--- a/src/core/meshcop/tcat_agent.hpp
+++ b/src/core/meshcop/tcat_agent.hpp
@@ -325,12 +325,6 @@
Error HandleSetActiveOperationalDataset(const Message &aIncommingMessage, uint16_t aOffset, uint16_t aLength);
Error HandleStartThreadInterface(void);
-#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
- void LogError(const char *aActionText, Error aError);
-#else
- void LogError(const char *, Error) {}
-#endif
-
bool CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags,
CommandClassFlags aDeviceCommandClassFlags,
Dataset *aDataset) const;
diff --git a/src/core/net/dhcp6_client.cpp b/src/core/net/dhcp6_client.cpp
index e206ff6..56654fb 100644
--- a/src/core/net/dhcp6_client.cpp
+++ b/src/core/net/dhcp6_client.cpp
@@ -288,7 +288,7 @@
if (error != kErrorNone)
{
FreeMessage(message);
- LogWarn("Failed to send DHCPv6 Solicit: %s", ErrorToString(error));
+ LogWarnOnError(error, "send DHCPv6 Solicit");
}
}
diff --git a/src/core/net/dhcp6_server.cpp b/src/core/net/dhcp6_server.cpp
index 7fd072c..ea95707 100644
--- a/src/core/net/dhcp6_server.cpp
+++ b/src/core/net/dhcp6_server.cpp
@@ -172,11 +172,8 @@
mPrefixAgentsCount++;
exit:
-
- if (error != kErrorNone)
- {
- LogNote("Failed to add DHCPv6 prefix agent: %s", ErrorToString(error));
- }
+ LogWarnOnError(error, "add DHCPv6 prefix agent");
+ OT_UNUSED_VARIABLE(error);
}
void Server::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
diff --git a/src/core/net/dnssd_server.cpp b/src/core/net/dnssd_server.cpp
index 385eb88..e05a7d9 100644
--- a/src/core/net/dnssd_server.cpp
+++ b/src/core/net/dnssd_server.cpp
@@ -170,7 +170,7 @@
ExitNow();
}
- LogWarn("Error forwarding to upstream: %s", ErrorToString(error));
+ LogWarnOnError(error, "forwarding to upstream");
rcode = Header::kResponseServerFailure;
diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp
index 0c0bf5a..d5b2e27 100644
--- a/src/core/net/ip6.cpp
+++ b/src/core/net/ip6.cpp
@@ -616,7 +616,7 @@
return error;
}
-Error Ip6::HandleFragment(Message &aMessage, MessageInfo &aMessageInfo)
+Error Ip6::HandleFragment(Message &aMessage)
{
Error error = kErrorNone;
Header header, headerBuffer;
@@ -703,8 +703,7 @@
mReassemblyList.Dequeue(*message);
- IgnoreError(HandleDatagram(OwnedPtr<Message>(message), aMessageInfo.mLinkInfo,
- /* aIsReassembled */ true));
+ IgnoreError(HandleDatagram(OwnedPtr<Message>(message), /* aIsReassembled */ true));
}
exit:
@@ -715,7 +714,7 @@
mReassemblyList.DequeueAndFree(*message);
}
- LogWarn("Reassembly failed: %s", ErrorToString(error));
+ LogWarnOnError(error, "reassemble");
}
if (isFragmented)
@@ -766,16 +765,12 @@
messageInfo.SetPeerAddr(header.GetSource());
messageInfo.SetSockAddr(header.GetDestination());
messageInfo.SetHopLimit(header.GetHopLimit());
- messageInfo.SetLinkInfo(nullptr);
error = mIcmp.SendError(aIcmpType, aIcmpCode, messageInfo, aMessage);
exit:
-
- if (error != kErrorNone)
- {
- LogWarn("Failed to send ICMP error: %s", ErrorToString(error));
- }
+ LogWarnOnError(error, "send ICMP");
+ OT_UNUSED_VARIABLE(error);
}
#else
@@ -788,10 +783,8 @@
return kErrorNone;
}
-Error Ip6::HandleFragment(Message &aMessage, MessageInfo &aMessageInfo)
+Error Ip6::HandleFragment(Message &aMessage)
{
- OT_UNUSED_VARIABLE(aMessageInfo);
-
Error error = kErrorNone;
FragmentHeader fragmentHeader;
@@ -829,7 +822,7 @@
case kProtoFragment:
IgnoreError(PassToHost(aMessagePtr, aMessageInfo, aNextHeader,
/* aApplyFilter */ false, aReceive, Message::kCopyToUse));
- SuccessOrExit(error = HandleFragment(*aMessagePtr, aMessageInfo));
+ SuccessOrExit(error = HandleFragment(*aMessagePtr));
break;
case kProtoDstOpts:
@@ -920,11 +913,7 @@
}
exit:
- if (error != kErrorNone)
- {
- LogNote("Failed to handle payload: %s", ErrorToString(error));
- }
-
+ LogWarnOnError(error, "handle payload");
return error;
}
@@ -959,17 +948,6 @@
if (mIsReceiveIp6FilterEnabled && aApplyFilter)
{
-#if !OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
- // Do not pass messages sent to an RLOC/ALOC, except
- // Service Locator
-
- bool isLocator = Get<Mle::Mle>().IsMeshLocalAddress(aMessageInfo.GetSockAddr()) &&
- aMessageInfo.GetSockAddr().GetIid().IsLocator();
-
- VerifyOrExit(!isLocator || aMessageInfo.GetSockAddr().GetIid().IsAnycastServiceLocator(),
- error = kErrorNoRoute);
-#endif
-
switch (aIpProto)
{
case kProtoIcmp6:
@@ -1094,7 +1072,7 @@
return error;
}
-Error Ip6::HandleDatagram(OwnedPtr<Message> aMessagePtr, const void *aLinkMessageInfo, bool aIsReassembled)
+Error Ip6::HandleDatagram(OwnedPtr<Message> aMessagePtr, bool aIsReassembled)
{
Error error;
MessageInfo messageInfo;
@@ -1115,7 +1093,6 @@
messageInfo.SetSockAddr(header.GetDestination());
messageInfo.SetHopLimit(header.GetHopLimit());
messageInfo.SetEcn(header.GetEcn());
- messageInfo.SetLinkInfo(aLinkMessageInfo);
// Determine `forwardThread`, `forwardHost` and `receive`
// based on the destination address.
@@ -1203,7 +1180,7 @@
Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageReceive, *messagePtr);
- IgnoreError(HandleDatagram(messagePtr.PassOwnership(), aLinkMessageInfo, aIsReassembled));
+ IgnoreError(HandleDatagram(messagePtr.PassOwnership(), aIsReassembled));
receive = false;
forwardHost = false;
diff --git a/src/core/net/ip6.hpp b/src/core/net/ip6.hpp
index 53330b3..de04eef 100644
--- a/src/core/net/ip6.hpp
+++ b/src/core/net/ip6.hpp
@@ -207,7 +207,6 @@
* Processes a received IPv6 datagram.
*
* @param[in] aMessage An owned pointer to a message.
- * @param[in] aLinkMessageInfo A pointer to link-specific message information.
*
* @retval kErrorNone Successfully processed the message.
* @retval kErrorDrop Message was well-formed but not fully processed due to packet processing rules.
@@ -216,9 +215,7 @@
* @retval kErrorParse Encountered a malformed header when processing the message.
*
*/
- Error HandleDatagram(OwnedPtr<Message> aMessagePtr,
- const void *aLinkMessageInfo = nullptr,
- bool aIsReassembled = false);
+ Error HandleDatagram(OwnedPtr<Message> aMessagePtr, bool aIsReassembled = false);
/**
* Registers a callback to provide received raw IPv6 datagrams.
@@ -378,7 +375,7 @@
uint8_t &aNextHeader,
bool &aReceive);
Error FragmentDatagram(Message &aMessage, uint8_t aIpProto);
- Error HandleFragment(Message &aMessage, MessageInfo &aMessageInfo);
+ Error HandleFragment(Message &aMessage);
#if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
void CleanupFragmentationBuffer(void);
void HandleTimeTick(void);
diff --git a/src/core/net/nd6.cpp b/src/core/net/nd6.cpp
index e66fb04..d73aea1 100644
--- a/src/core/net/nd6.cpp
+++ b/src/core/net/nd6.cpp
@@ -36,6 +36,7 @@
#include "common/as_core_type.hpp"
#include "common/code_utils.hpp"
+#include "instance/instance.hpp"
namespace ot {
namespace Ip6 {
@@ -181,9 +182,9 @@
}
//----------------------------------------------------------------------------------------------------------------------
-// RouterAdverMessage::Header
+// RouterAdver::Header
-void RouterAdvertMessage::Header::SetToDefault(void)
+void RouterAdvert::Header::SetToDefault(void)
{
OT_UNUSED_VARIABLE(mCode);
OT_UNUSED_VARIABLE(mCurHopLimit);
@@ -194,21 +195,21 @@
mType = Icmp::Header::kTypeRouterAdvert;
}
-RoutePreference RouterAdvertMessage::Header::GetDefaultRouterPreference(void) const
+RoutePreference RouterAdvert::Header::GetDefaultRouterPreference(void) const
{
return NetworkData::RoutePreferenceFromValue((mFlags & kPreferenceMask) >> kPreferenceOffset);
}
-void RouterAdvertMessage::Header::SetDefaultRouterPreference(RoutePreference aPreference)
+void RouterAdvert::Header::SetDefaultRouterPreference(RoutePreference aPreference)
{
mFlags &= ~kPreferenceMask;
mFlags |= (NetworkData::RoutePreferenceToValue(aPreference) << kPreferenceOffset) & kPreferenceMask;
}
//----------------------------------------------------------------------------------------------------------------------
-// RouterAdverMessage
+// RouterAdver::TxMessage
-Option *RouterAdvertMessage::AppendOption(uint16_t aOptionSize)
+Option *RouterAdvert::TxMessage::AppendOption(uint16_t aOptionSize)
{
// This method appends an option with a given size to the RA
// message by reserving space in the data buffer if there is
@@ -217,21 +218,39 @@
// initialized and populated by the caller.
Option *option = nullptr;
- uint32_t newLength = mData.GetLength();
+ uint16_t oldLength = mArray.GetLength();
- newLength += aOptionSize;
- VerifyOrExit(newLength <= mMaxLength);
-
- option = reinterpret_cast<Option *>(AsNonConst(GetDataEnd()));
- mData.SetLength(static_cast<uint16_t>(newLength));
+ SuccessOrExit(AppendBytes(nullptr, aOptionSize));
+ option = reinterpret_cast<Option *>(&mArray[oldLength]);
exit:
return option;
}
-Error RouterAdvertMessage::AppendPrefixInfoOption(const Prefix &aPrefix,
- uint32_t aValidLifetime,
- uint32_t aPreferredLifetime)
+Error RouterAdvert::TxMessage::AppendBytes(const uint8_t *aBytes, uint16_t aLength)
+{
+ Error error = kErrorNone;
+
+ for (; aLength > 0; aLength--)
+ {
+ uint8_t byte;
+
+ byte = (aBytes == nullptr) ? 0 : *aBytes++;
+ SuccessOrExit(error = mArray.PushBack(byte));
+ }
+
+exit:
+ return error;
+}
+
+Error RouterAdvert::TxMessage::AppendHeader(const Header &aHeader)
+{
+ return AppendBytes(reinterpret_cast<const uint8_t *>(&aHeader), sizeof(Header));
+}
+
+Error RouterAdvert::TxMessage::AppendPrefixInfoOption(const Prefix &aPrefix,
+ uint32_t aValidLifetime,
+ uint32_t aPreferredLifetime)
{
Error error = kErrorNone;
PrefixInfoOption *pio;
@@ -250,9 +269,9 @@
return error;
}
-Error RouterAdvertMessage::AppendRouteInfoOption(const Prefix &aPrefix,
- uint32_t aRouteLifetime,
- RoutePreference aPreference)
+Error RouterAdvert::TxMessage::AppendRouteInfoOption(const Prefix &aPrefix,
+ uint32_t aRouteLifetime,
+ RoutePreference aPreference)
{
Error error = kErrorNone;
RouteInfoOption *rio;
@@ -269,7 +288,7 @@
return error;
}
-Error RouterAdvertMessage::AppendFlagsExtensionOption(bool aStubRouterFlag)
+Error RouterAdvert::TxMessage::AppendFlagsExtensionOption(bool aStubRouterFlag)
{
Error error = kErrorNone;
RaFlagsExtOption *flagsOption;
diff --git a/src/core/net/nd6.hpp b/src/core/net/nd6.hpp
index f4a6d5a..4178f18 100644
--- a/src/core/net/nd6.hpp
+++ b/src/core/net/nd6.hpp
@@ -47,6 +47,7 @@
#include "common/const_cast.hpp"
#include "common/encoding.hpp"
#include "common/equatable.hpp"
+#include "common/heap_array.hpp"
#include "net/icmp6.hpp"
#include "net/ip6.hpp"
#include "thread/network_data_types.hpp"
@@ -67,7 +68,7 @@
OT_TOOL_PACKED_BEGIN
class Option
{
- friend class RouterAdvertMessage;
+ friend class RouterAdvert;
public:
enum Type : uint8_t
@@ -525,14 +526,14 @@
static_assert(sizeof(RaFlagsExtOption) == 8, "invalid RaFlagsExtOption structure");
/**
- * Represents a Router Advertisement message.
+ * Defines Router Advertisement components.
*
*/
-class RouterAdvertMessage
+class RouterAdvert
{
public:
/**
- * Implements the RA message header.
+ * Represent an RA message header.
*
* See section 2.2 of RFC 4191 [https://datatracker.ietf.org/doc/html/rfc4191]
*
@@ -673,135 +674,170 @@
typedef Data<kWithUint16Length> Icmp6Packet; ///< A data buffer containing an ICMPv6 packet.
/**
- * Initializes the RA message from a received packet data buffer.
- *
- * @param[in] aPacket A received packet data.
+ * Represents a received RA message.
*
*/
- explicit RouterAdvertMessage(const Icmp6Packet &aPacket)
- : mData(aPacket)
- , mMaxLength(0)
+ class RxMessage
{
- }
+ public:
+ /**
+ * Initializes the RA message from a received packet data buffer.
+ *
+ * @param[in] aPacket A received packet data.
+ *
+ */
+ explicit RxMessage(const Icmp6Packet &aPacket)
+ : mData(aPacket)
+ {
+ }
+
+ /**
+ * Gets the RA message as an `Icmp6Packet`.
+ *
+ * @returns The RA message as an `Icmp6Packet`.
+ *
+ */
+ const Icmp6Packet &GetAsPacket(void) const { return mData; }
+
+ /**
+ * Indicates whether or not the received RA message is valid.
+ *
+ * @retval TRUE If the RA message is valid.
+ * @retval FALSE If the RA message is not valid.
+ *
+ */
+ bool IsValid(void) const
+ {
+ return (mData.GetBytes() != nullptr) && (mData.GetLength() >= sizeof(Header)) &&
+ (GetHeader().GetType() == Icmp::Header::kTypeRouterAdvert);
+ }
+
+ /**
+ * Gets the RA message's header.
+ *
+ * @returns The RA message's header.
+ *
+ */
+ const Header &GetHeader(void) const { return *reinterpret_cast<const Header *>(mData.GetBytes()); }
+
+ /**
+ * Indicates whether or not the received RA message contains any options.
+ *
+ * @retval TRUE If the RA message contains at least one option.
+ * @retval FALSE If the RA message contains no options.
+ *
+ */
+ bool ContainsAnyOptions(void) const { return (mData.GetLength() > sizeof(Header)); }
+
+ // The following methods are intended to support range-based `for`
+ // loop iteration over `Option`s in the RA message.
+
+ Option::Iterator begin(void) const { return Option::Iterator(GetOptionStart(), GetDataEnd()); }
+ Option::Iterator end(void) const { return Option::Iterator(); }
+
+ private:
+ const uint8_t *GetOptionStart(void) const { return (mData.GetBytes() + sizeof(Header)); }
+ const uint8_t *GetDataEnd(void) const { return mData.GetBytes() + mData.GetLength(); }
+
+ Data<kWithUint16Length> mData;
+ };
/**
- * This template constructor initializes the RA message with a given header using a given buffer to store the RA
- * message.
- *
- * @tparam kBufferSize The size of the buffer used to store the RA message.
- *
- * @param[in] aHeader The RA message header.
- * @param[in] aBuffer The data buffer to store the RA message in.
+ * Represents an RA message to be sent.
*
*/
- template <uint16_t kBufferSize>
- RouterAdvertMessage(const Header &aHeader, uint8_t (&aBuffer)[kBufferSize])
- : mMaxLength(kBufferSize)
+ class TxMessage
{
- static_assert(kBufferSize >= sizeof(Header), "Buffer for RA msg is too small");
+ public:
+ /**
+ * Gets the prepared RA message as an `Icmp6Packet`.
+ *
+ * @param[out] aPacket A reference to an `Icmp6Packet`.
+ *
+ */
+ void GetAsPacket(Icmp6Packet &aPacket) const { aPacket.Init(mArray.AsCArray(), mArray.GetLength()); }
- memcpy(aBuffer, &aHeader, sizeof(Header));
- mData.Init(aBuffer, sizeof(Header));
- }
+ /**
+ * Appends the RA header.
+ *
+ * @param[in] aHeader The RA header.
+ *
+ * @retval kErrorNone Header is written successfully.
+ * @retval kErrorNoBufs Insufficient available buffers to grow the message.
+ *
+ */
+ Error AppendHeader(const Header &aHeader);
- /**
- * Gets the RA message as an `Icmp6Packet`.
- *
- * @returns The RA message as an `Icmp6Packet`.
- *
- */
- const Icmp6Packet &GetAsPacket(void) const { return mData; }
+ /**
+ * Appends a Prefix Info Option to the RA message.
+ *
+ * The appended Prefix Info Option will have both on-link (L) and autonomous address-configuration (A) flags
+ * set.
+ *
+ * @param[in] aPrefix The prefix.
+ * @param[in] aValidLifetime The valid lifetime in seconds.
+ * @param[in] aPreferredLifetime The preferred lifetime in seconds.
+ *
+ * @retval kErrorNone Option is appended successfully.
+ * @retval kErrorNoBufs Insufficient available buffers to grow the message.
+ *
+ */
+ Error AppendPrefixInfoOption(const Prefix &aPrefix, uint32_t aValidLifetime, uint32_t aPreferredLifetime);
- /**
- * Indicates whether or not the RA message is valid.
- *
- * @retval TRUE If the RA message is valid.
- * @retval FALSE If the RA message is not valid.
- *
- */
- bool IsValid(void) const
- {
- return (mData.GetBytes() != nullptr) && (mData.GetLength() >= sizeof(Header)) &&
- (GetHeader().GetType() == Icmp::Header::kTypeRouterAdvert);
- }
+ /**
+ * Appends a Route Info Option to the RA message.
+ *
+ * @param[in] aPrefix The prefix.
+ * @param[in] aRouteLifetime The route lifetime in seconds.
+ * @param[in] aPreference The route preference.
+ *
+ * @retval kErrorNone Option is appended successfully.
+ * @retval kErrorNoBufs Insufficient available buffers to grow the message.
+ *
+ */
+ Error AppendRouteInfoOption(const Prefix &aPrefix, uint32_t aRouteLifetime, RoutePreference aPreference);
- /**
- * Gets the RA message's header.
- *
- * @returns The RA message's header.
- *
- */
- const Header &GetHeader(void) const { return *reinterpret_cast<const Header *>(mData.GetBytes()); }
+ /**
+ * Appends a Flags Extension Option to the RA message.
+ *
+ * @param[in] aStubRouterFlag The stub router flag.
+ *
+ * @retval kErrorNone Option is appended successfully.
+ * @retval kErrorNoBufs Insufficient available buffers to grow the message.
+ *
+ */
+ Error AppendFlagsExtensionOption(bool aStubRouterFlag);
- /**
- * Gets the RA message's header.
- *
- * @returns The RA message's header.
- *
- */
- Header &GetHeader(void) { return *reinterpret_cast<Header *>(AsNonConst(mData.GetBytes())); }
+ /**
+ * Appends bytes from a given buffer to the RA message.
+ *
+ * @param[in] aBytes A pointer to the buffer containing the bytes to append.
+ * @param[in] aLength The buffer length.
+ *
+ * @retval kErrorNone Bytes are appended successfully.
+ * @retval kErrorNoBufs Insufficient available buffers to grow the message.
+ *
+ */
+ Error AppendBytes(const uint8_t *aBytes, uint16_t aLength);
- /**
- * Appends a Prefix Info Option to the RA message.
- *
- * The appended Prefix Info Option will have both on-link (L) and autonomous address-configuration (A) flags set.
- *
- * @param[in] aPrefix The prefix.
- * @param[in] aValidLifetime The valid lifetime in seconds.
- * @param[in] aPreferredLifetime The preferred lifetime in seconds.
- *
- * @retval kErrorNone Option is appended successfully.
- * @retval kErrorNoBufs No more space in the buffer to append the option.
- *
- */
- Error AppendPrefixInfoOption(const Prefix &aPrefix, uint32_t aValidLifetime, uint32_t aPreferredLifetime);
+ /**
+ * Indicates whether or not the received RA message contains any options.
+ *
+ * @retval TRUE If the RA message contains at least one option.
+ * @retval FALSE If the RA message contains no options.
+ *
+ */
+ bool ContainsAnyOptions(void) const { return (mArray.GetLength() > sizeof(Header)); }
- /**
- * Appends a Route Info Option to the RA message.
- *
- * @param[in] aPrefix The prefix.
- * @param[in] aRouteLifetime The route lifetime in seconds.
- * @param[in] aPreference The route preference.
- *
- * @retval kErrorNone Option is appended successfully.
- * @retval kErrorNoBufs No more space in the buffer to append the option.
- *
- */
- Error AppendRouteInfoOption(const Prefix &aPrefix, uint32_t aRouteLifetime, RoutePreference aPreference);
+ private:
+ static constexpr uint16_t kCapacityIncrement = 256;
- /**
- * Appends a Flags Extension Option to the RA message.
- *
- * @param[in] aStubRouterFlag The stub router flag.
- *
- * @retval kErrorNone Option is appended successfully.
- * @retval kErrorNoBufs No more space in the buffer to append the option.
- *
- */
- Error AppendFlagsExtensionOption(bool aStubRouterFlag);
+ Option *AppendOption(uint16_t aOptionSize);
- /**
- * Indicates whether or not the RA message contains any options.
- *
- * @retval TRUE If the RA message contains at least one option.
- * @retval FALSE If the RA message contains no options.
- *
- */
- bool ContainsAnyOptions(void) const { return (mData.GetLength() > sizeof(Header)); }
+ Heap::Array<uint8_t, kCapacityIncrement> mArray;
+ };
- // The following methods are intended to support range-based `for`
- // loop iteration over `Option`s in the RA message.
-
- Option::Iterator begin(void) const { return Option::Iterator(GetOptionStart(), GetDataEnd()); }
- Option::Iterator end(void) const { return Option::Iterator(); }
-
-private:
- const uint8_t *GetOptionStart(void) const { return (mData.GetBytes() + sizeof(Header)); }
- const uint8_t *GetDataEnd(void) const { return mData.GetBytes() + mData.GetLength(); }
- Option *AppendOption(uint16_t aOptionSize);
-
- Data<kWithUint16Length> mData;
- uint16_t mMaxLength;
+ RouterAdvert(void) = delete;
};
/**
diff --git a/src/core/net/sntp_client.cpp b/src/core/net/sntp_client.cpp
index 5897a5f..7718b02 100644
--- a/src/core/net/sntp_client.cpp
+++ b/src/core/net/sntp_client.cpp
@@ -193,7 +193,7 @@
if (error != kErrorNone)
{
FreeMessage(messageCopy);
- LogWarn("Failed to send SNTP request: %s", ErrorToString(error));
+ LogWarnOnError(error, "send SNTP request");
}
}
diff --git a/src/core/net/socket.hpp b/src/core/net/socket.hpp
index fb4e928..e1ca9ac 100644
--- a/src/core/net/socket.hpp
+++ b/src/core/net/socket.hpp
@@ -181,30 +181,6 @@
void SetMulticastLoop(bool aMulticastLoop) { mMulticastLoop = aMulticastLoop; }
/**
- * Returns a pointer to the link-specific information object.
- *
- * @returns A pointer to the link-specific information object.
- *
- */
- const void *GetLinkInfo(void) const { return mLinkInfo; }
-
- /**
- * Sets the pointer to the link-specific information object.
- *
- * @param[in] aLinkInfo A pointer to the link-specific information object.
- *
- */
- void SetLinkInfo(const void *aLinkInfo) { mLinkInfo = aLinkInfo; }
-
- /**
- * Returns a pointer to the link-specific information as a `ThreadLinkInfo`.
- *
- * @returns A pointer to to the link-specific information object as `ThreadLinkInfo`.
- *
- */
- const ThreadLinkInfo *GetThreadLinkInfo(void) const { return reinterpret_cast<const ThreadLinkInfo *>(mLinkInfo); }
-
- /**
* Gets the ECN status.
*
* @returns The ECN status, as represented in the IP header.
diff --git a/src/core/net/srp_server.cpp b/src/core/net/srp_server.cpp
index 3d6550d..635cbed 100644
--- a/src/core/net/srp_server.cpp
+++ b/src/core/net/srp_server.cpp
@@ -91,7 +91,7 @@
, mOutstandingUpdatesTimer(aInstance)
, mCompletedUpdateTask(aInstance)
, mServiceUpdateId(Random::NonCrypto::GetUint32())
- , mPort(kUdpPortMin)
+ , mPort(kUninitializedPort)
, mState(kStateDisabled)
, mAddressMode(kDefaultAddressMode)
, mAnycastSequenceNumber(0)
@@ -595,7 +595,7 @@
}
}
-void Server::SelectPort(void)
+void Server::InitPort(void)
{
mPort = kUdpPortMin;
@@ -605,24 +605,35 @@
if (Get<Settings>().Read(info) == kErrorNone)
{
- mPort = info.GetPort() + 1;
- if (mPort < kUdpPortMin || mPort > kUdpPortMax)
- {
- mPort = kUdpPortMin;
- }
+ mPort = info.GetPort();
}
}
#endif
+}
+
+void Server::SelectPort(void)
+{
+ if (mPort == kUninitializedPort)
+ {
+ InitPort();
+ }
+ ++mPort;
+ if (mPort < kUdpPortMin || mPort > kUdpPortMax)
+ {
+ mPort = kUdpPortMin;
+ }
LogInfo("Selected port %u", mPort);
}
void Server::Start(void)
{
+ Error error = kErrorNone;
+
VerifyOrExit(mState == kStateStopped);
mState = kStateRunning;
- PrepareSocket();
+ SuccessOrExit(error = PrepareSocket());
LogInfo("Start listening on port %u", mPort);
#if OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE
@@ -630,10 +641,15 @@
#endif
exit:
- return;
+ // Re-enable server to select a new port.
+ if (error != kErrorNone)
+ {
+ Disable();
+ Enable();
+ }
}
-void Server::PrepareSocket(void)
+Error Server::PrepareSocket(void)
{
Error error = kErrorNone;
@@ -658,9 +674,12 @@
exit:
if (error != kErrorNone)
{
- LogCrit("Failed to prepare socket: %s", ErrorToString(error));
+ LogWarnOnError(error, "prepare socket");
+ IgnoreError(mSocket.Close());
Stop();
}
+
+ return error;
}
Ip6::Udp::Socket &Server::GetSocket(void)
@@ -689,7 +708,7 @@
if (mState == kStateRunning)
{
- PrepareSocket();
+ IgnoreError(PrepareSocket());
}
}
@@ -811,6 +830,13 @@
// Parse lease time and validate signature.
SuccessOrExit(error = ProcessAdditionalSection(host, aMessage, aMetadata));
+#if OPENTHREAD_FTD
+ if (aMetadata.IsDirectRxFromClient())
+ {
+ UpdateAddrResolverCacheTable(*aMetadata.mMessageInfo, *host);
+ }
+#endif
+
HandleUpdate(*host, aMetadata);
exit:
@@ -846,11 +872,7 @@
aMetadata.mOffset = offset;
exit:
- if (error != kErrorNone)
- {
- LogWarn("Failed to process DNS Zone section: %s", ErrorToString(error));
- }
-
+ LogWarnOnError(error, "process DNS Zone section");
return error;
}
@@ -876,11 +898,7 @@
VerifyOrExit(!HasNameConflictsWith(aHost), error = kErrorDuplicated);
exit:
- if (error != kErrorNone)
- {
- LogWarn("Failed to process DNS Update section: %s", ErrorToString(error));
- }
-
+ LogWarnOnError(error, "Process DNS Update section");
return error;
}
@@ -969,11 +987,7 @@
// the host is being removed or registered.
exit:
- if (error != kErrorNone)
- {
- LogWarn("Failed to process Host Description instructions: %s", ErrorToString(error));
- }
-
+ LogWarnOnError(error, "process Host Description instructions");
return error;
}
@@ -1092,11 +1106,7 @@
}
exit:
- if (error != kErrorNone)
- {
- LogWarn("Failed to process Service Discovery instructions: %s", ErrorToString(error));
- }
-
+ LogWarnOnError(error, "process Service Discovery instructions");
return error;
}
@@ -1195,11 +1205,7 @@
aMetadata.mOffset = offset;
exit:
- if (error != kErrorNone)
- {
- LogWarn("Failed to process Service Description instructions: %s", ErrorToString(error));
- }
-
+ LogWarnOnError(error, "process Service Description instructions");
return error;
}
@@ -1288,11 +1294,7 @@
aMetadata.mOffset = offset;
exit:
- if (error != kErrorNone)
- {
- LogWarn("Failed to process DNS Additional section: %s", ErrorToString(error));
- }
-
+ LogWarnOnError(error, "process DNS Additional section");
return error;
}
@@ -1339,11 +1341,7 @@
error = aKey.Verify(hash, signature);
exit:
- if (error != kErrorNone)
- {
- LogWarn("Failed to verify message signature: %s", ErrorToString(error));
- }
-
+ LogWarnOnError(error, "verify message signature");
FreeMessage(signerNameMessage);
return error;
}
@@ -1503,11 +1501,8 @@
UpdateResponseCounters(aResponseCode);
exit:
- if (error != kErrorNone)
- {
- LogWarn("Failed to send response: %s", ErrorToString(error));
- FreeMessage(response);
- }
+ LogWarnOnError(error, "send response");
+ FreeMessageOnError(response, error);
}
void Server::SendResponse(const Dns::UpdateHeader &aHeader,
@@ -1562,11 +1557,8 @@
UpdateResponseCounters(Dns::UpdateHeader::kResponseSuccess);
exit:
- if (error != kErrorNone)
- {
- LogWarn("Failed to send response: %s", ErrorToString(error));
- FreeMessage(response);
- }
+ LogWarnOnError(error, "send response");
+ FreeMessageOnError(response, error);
}
void Server::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
@@ -1578,10 +1570,8 @@
{
Error error = ProcessMessage(aMessage, aMessageInfo);
- if (error != kErrorNone)
- {
- LogInfo("Failed to handle DNS message: %s", ErrorToString(error));
- }
+ LogWarnOnError(error, "handle DNS message");
+ OT_UNUSED_VARIABLE(error);
}
Error Server::ProcessMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
@@ -1789,6 +1779,41 @@
}
}
+#if OPENTHREAD_FTD
+void Server::UpdateAddrResolverCacheTable(const Ip6::MessageInfo &aMessageInfo, const Host &aHost)
+{
+ // If message is from a client on mesh, we add all registered
+ // addresses as snooped entries in the address resolver cache
+ // table. We associate the registered addresses with the same
+ // RLOC16 (if any) as the received message's peer IPv6 address.
+
+ uint16_t rloc16;
+
+ VerifyOrExit(aHost.GetLease() != 0);
+ VerifyOrExit(aHost.GetTtl() > 0);
+
+ // If the `LookUp()` call succeeds, the cache entry will be marked
+ // as "cached and in-use". We can mark it as "in-use" early since
+ // the entry will be needed and used soon when sending the SRP
+ // response. This also prevents a snooped cache entry (added for
+ // `GetPeerAddr()` due to rx of the SRP update message) from
+ // being overwritten by `UpdateSnoopedCacheEntry()` calls when
+ // there are limited snoop entries available.
+
+ rloc16 = Get<AddressResolver>().LookUp(aMessageInfo.GetPeerAddr());
+
+ VerifyOrExit(rloc16 != Mac::kShortAddrInvalid);
+
+ for (const Ip6::Address &address : aHost.mAddresses)
+ {
+ Get<AddressResolver>().UpdateSnoopedCacheEntry(address, rloc16, Get<Mle::Mle>().GetRloc16());
+ }
+
+exit:
+ return;
+}
+#endif
+
//---------------------------------------------------------------------------------------------------------------------
// Server::Service
diff --git a/src/core/net/srp_server.hpp b/src/core/net/srp_server.hpp
index 6b5556d..ebe67d8 100644
--- a/src/core/net/srp_server.hpp
+++ b/src/core/net/srp_server.hpp
@@ -912,6 +912,7 @@
static constexpr AddressMode kDefaultAddressMode =
static_cast<AddressMode>(OPENTHREAD_CONFIG_SRP_SERVER_DEFAULT_ADDRESS_MODE);
+ static constexpr uint16_t kUninitializedPort = 0;
static constexpr uint16_t kAnycastAddressModePort = 53;
// Metadata for a received SRP Update message.
@@ -971,8 +972,9 @@
void Disable(void);
void Start(void);
void Stop(void);
+ void InitPort(void);
void SelectPort(void);
- void PrepareSocket(void);
+ Error PrepareSocket(void);
Ip6::Udp::Socket &GetSocket(void);
LinkedList<Host> &GetHosts(void) { return mHosts; }
@@ -1043,6 +1045,7 @@
static const char *AddressModeToString(AddressMode aMode);
void UpdateResponseCounters(Dns::Header::Response aResponseCode);
+ void UpdateAddrResolverCacheTable(const Ip6::MessageInfo &aMessageInfo, const Host &aHost);
using LeaseTimer = TimerMilliIn<Server, &Server::HandleLeaseTimer>;
using UpdateTimer = TimerMilliIn<Server, &Server::HandleOutstandingUpdatesTimer>;
diff --git a/src/core/openthread-core-config.h b/src/core/openthread-core-config.h
index 498eb62..009b671 100644
--- a/src/core/openthread-core-config.h
+++ b/src/core/openthread-core-config.h
@@ -41,7 +41,9 @@
#define OT_THREAD_VERSION_1_1 2
#define OT_THREAD_VERSION_1_2 3
#define OT_THREAD_VERSION_1_3 4
+// Support projects on legacy "1.3.1" version, which is now "1.4"
#define OT_THREAD_VERSION_1_3_1 5
+#define OT_THREAD_VERSION_1_4 5
#define OPENTHREAD_CORE_CONFIG_H_IN
diff --git a/src/core/radio/ble_secure.cpp b/src/core/radio/ble_secure.cpp
index 5e52244..8f4a0a9 100644
--- a/src/core/radio/ble_secure.cpp
+++ b/src/core/radio/ble_secure.cpp
@@ -89,7 +89,14 @@
Error BleSecure::TcatStart(const MeshCoP::TcatAgent::VendorInfo &aVendorInfo,
MeshCoP::TcatAgent::JoinCallback aJoinHandler)
{
- return mTcatAgent.Start(aVendorInfo, mReceiveCallback.GetHandler(), aJoinHandler, mReceiveCallback.GetContext());
+ Error error;
+
+ VerifyOrExit(mBleState != kStopped, error = kErrorInvalidState);
+
+ error = mTcatAgent.Start(aVendorInfo, mReceiveCallback.GetHandler(), aJoinHandler, mReceiveCallback.GetContext());
+
+exit:
+ return error;
}
void BleSecure::Stop(void)
@@ -124,8 +131,14 @@
Error BleSecure::Connect(void)
{
Ip6::SockAddr sockaddr;
+ Error error;
- return mTls.Connect(sockaddr);
+ VerifyOrExit(mBleState == kConnected, error = kErrorInvalidState);
+
+ error = mTls.Connect(sockaddr);
+
+exit:
+ return error;
}
void BleSecure::Disconnect(void)
@@ -137,8 +150,11 @@
if (mBleState == kConnected)
{
+ mBleState = kAdvertising;
IgnoreReturnValue(otPlatBleGapDisconnect(&GetInstance()));
}
+
+ mConnectCallback.InvokeIfSet(&GetInstance(), false, false);
}
void BleSecure::SetPsk(const MeshCoP::JoinerPskd &aPskd)
@@ -278,12 +294,7 @@
mBleState = kAdvertising;
mMtuSize = kInitialMtuSize;
- if (IsConnected())
- {
- Disconnect(); // Stop TLS connection
- }
-
- mConnectCallback.InvokeIfSet(&GetInstance(), false, false);
+ Disconnect(); // Stop TLS connection
}
Error BleSecure::HandleBleMtuUpdate(uint16_t aMtu)
diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp
index 1805161..c8bb938 100644
--- a/src/core/radio/radio.hpp
+++ b/src/core/radio/radio.hpp
@@ -37,11 +37,12 @@
#include "openthread-core-config.h"
#include <openthread/radio_stats.h>
+#include <openthread/platform/crypto.h>
#include <openthread/platform/radio.h>
-#include <openthread/platform/crypto.h>
#include "common/locator.hpp"
#include "common/non_copyable.hpp"
+#include "common/numeric_limits.hpp"
#include "common/time.hpp"
#include "mac/mac_frame.hpp"
@@ -1082,9 +1083,9 @@
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
-inline uint8_t Radio::GetCslAccuracy(void) { return UINT8_MAX; }
+inline uint8_t Radio::GetCslAccuracy(void) { return NumericLimits<uint8_t>::kMax; }
-inline uint8_t Radio::GetCslUncertainty(void) { return UINT8_MAX; }
+inline uint8_t Radio::GetCslUncertainty(void) { return NumericLimits<uint8_t>::kMax; }
#endif
inline Mac::TxFrame &Radio::GetTransmitBuffer(void)
diff --git a/src/core/radio/radio_platform.cpp b/src/core/radio/radio_platform.cpp
index 6dcb85d..0f60012 100644
--- a/src/core/radio/radio_platform.cpp
+++ b/src/core/radio/radio_platform.cpp
@@ -261,14 +261,14 @@
{
OT_UNUSED_VARIABLE(aInstance);
- return UINT8_MAX;
+ return NumericLimits<uint8_t>::kMax;
}
OT_TOOL_WEAK uint8_t otPlatRadioGetCslUncertainty(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
- return UINT8_MAX;
+ return NumericLimits<uint8_t>::kMax;
}
OT_TOOL_WEAK otError otPlatRadioGetFemLnaGain(otInstance *aInstance, int8_t *aGain)
diff --git a/src/core/thread/address_resolver.hpp b/src/core/thread/address_resolver.hpp
index 880d436..8b5d0a4 100644
--- a/src/core/thread/address_resolver.hpp
+++ b/src/core/thread/address_resolver.hpp
@@ -199,7 +199,10 @@
/**
* Looks up the RLOC16 for a given EID in the address cache.
*
- * @param[in] aEid A reference to the EID.
+ * When a cache entry is successfully looked up using this method, it will be marked as "cached and in-use".
+ * Specifically, a snooped entry (`kStateSnooped`) will be marked as cached (`kStateCached`).
+ *
+ * @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.
*
diff --git a/src/core/thread/csl_tx_scheduler.hpp b/src/core/thread/csl_tx_scheduler.hpp
index 77eebcd..7b3d129 100644
--- a/src/core/thread/csl_tx_scheduler.hpp
+++ b/src/core/thread/csl_tx_scheduler.hpp
@@ -113,7 +113,7 @@
* containing the CSL IE was transmitted until the next channel sample,
* see IEEE 802.15.4-2015, section 6.12.2.
*
- * The Thread standard further defines the CSL phase (see Thread 1.3.1,
+ * The Thread standard further defines the CSL phase (see Thread 1.4,
* section 3.2.6.3.4, also conforming to IEEE 802.15.4-2020, section
* 6.12.2.1):
* * The "first symbol" from the definition SHALL be interpreted as the
diff --git a/src/core/thread/discover_scanner.cpp b/src/core/thread/discover_scanner.cpp
index 3a59651..9cd9978 100644
--- a/src/core/thread/discover_scanner.cpp
+++ b/src/core/thread/discover_scanner.cpp
@@ -308,8 +308,7 @@
void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const
{
- Error error = kErrorNone;
- const ThreadLinkInfo *linkInfo = aRxInfo.mMessageInfo.GetThreadLinkInfo();
+ Error error = kErrorNone;
MeshCoP::Tlv meshcopTlv;
MeshCoP::DiscoveryResponseTlv discoveryResponse;
MeshCoP::NetworkNameTlv networkName;
@@ -327,10 +326,10 @@
ClearAllBytes(result);
result.mDiscover = true;
- result.mPanId = linkInfo->mPanId;
- result.mChannel = linkInfo->mChannel;
- result.mRssi = linkInfo->mRss;
- result.mLqi = linkInfo->mLqi;
+ result.mPanId = aRxInfo.mMessage.GetPanId();
+ result.mChannel = aRxInfo.mMessage.GetChannel();
+ result.mRssi = aRxInfo.mMessage.GetAverageRss();
+ result.mLqi = aRxInfo.mMessage.GetAverageLqi();
aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(AsCoreType(&result.mExtAddress));
diff --git a/src/core/thread/dua_manager.cpp b/src/core/thread/dua_manager.cpp
index 7d78879..9f98b07 100644
--- a/src/core/thread/dua_manager.cpp
+++ b/src/core/thread/dua_manager.cpp
@@ -158,7 +158,7 @@
}
else
{
- LogWarn("Generate DUA: %s", ErrorToString(error));
+ LogWarnOnError(error, "generate DUA");
}
return error;
@@ -548,7 +548,7 @@
UpdateCheckDelay(kNoBufDelay);
}
- LogInfo("PerformNextRegistration: %s", ErrorToString(error));
+ LogWarnOnError(error, "perform next registration");
FreeMessageOnError(message, error);
}
diff --git a/src/core/thread/energy_scan_server.cpp b/src/core/thread/energy_scan_server.cpp
index 2585805..c79c7d8 100644
--- a/src/core/thread/energy_scan_server.cpp
+++ b/src/core/thread/energy_scan_server.cpp
@@ -198,7 +198,7 @@
exit:
FreeMessageOnError(mReportMessage, error);
- MeshCoP::LogError("send scan results", error);
+ LogWarnOnError(error, "send scan results");
mReportMessage = nullptr;
}
diff --git a/src/core/thread/key_manager.cpp b/src/core/thread/key_manager.cpp
index 3d9337d..d38fdb8 100644
--- a/src/core/thread/key_manager.cpp
+++ b/src/core/thread/key_manager.cpp
@@ -60,6 +60,9 @@
'r', 'I', 'n', 'f', 'r', 'a', 'K', 'e', 'y'};
#endif
+//---------------------------------------------------------------------------------------------------------------------
+// SecurityPolicy
+
void SecurityPolicy::SetToDefault(void)
{
mRotationTime = kDefaultKeyRotationTime;
@@ -163,6 +166,9 @@
return;
}
+//---------------------------------------------------------------------------------------------------------------------
+// KeyManager
+
KeyManager::KeyManager(Instance &aInstance)
: InstanceLocator(aInstance)
, mKeySequence(0)
@@ -171,7 +177,7 @@
, mStoredMleFrameCounter(0)
, mHoursSinceKeyRotation(0)
, mKeySwitchGuardTime(kDefaultKeySwitchGuardTime)
- , mKeySwitchGuardEnabled(false)
+ , mKeySwitchGuardTimer(0)
, mKeyRotationTimer(aInstance)
, mKekFrameCounter(0)
, mIsPskcSet(false)
@@ -198,8 +204,8 @@
void KeyManager::Start(void)
{
- mKeySwitchGuardEnabled = false;
- StartKeyRotationTimer();
+ mKeySwitchGuardTimer = 0;
+ ResetKeyRotationTimer();
}
void KeyManager::Stop(void) { mKeyRotationTimer.Stop(); }
@@ -362,20 +368,13 @@
#endif
}
-void KeyManager::SetCurrentKeySequence(uint32_t aKeySequence)
+void KeyManager::SetCurrentKeySequence(uint32_t aKeySequence, KeySequenceUpdateMode aUpdateMode)
{
VerifyOrExit(aKeySequence != mKeySequence, Get<Notifier>().SignalIfFirst(kEventThreadKeySeqCounterChanged));
- if ((aKeySequence == (mKeySequence + 1)) && mKeyRotationTimer.IsRunning())
+ if (aUpdateMode == kApplyKeySwitchGuard)
{
- if (mKeySwitchGuardEnabled)
- {
- // Check if the guard timer has expired if key rotation is requested.
- VerifyOrExit(mHoursSinceKeyRotation >= mKeySwitchGuardTime);
- StartKeyRotationTimer();
- }
-
- mKeySwitchGuardEnabled = true;
+ VerifyOrExit(mKeySwitchGuardTimer == 0);
}
mKeySequence = aKeySequence;
@@ -384,6 +383,9 @@
SetAllMacFrameCounters(0, /* aSetIfLarger */ false);
mMleFrameCounter = 0;
+ ResetKeyRotationTimer();
+ mKeySwitchGuardTimer = mKeySwitchGuardTime;
+
Get<Notifier>().Signal(kEventThreadKeySeqCounterChanged);
exit:
@@ -476,40 +478,57 @@
void KeyManager::SetSecurityPolicy(const SecurityPolicy &aSecurityPolicy)
{
- if (aSecurityPolicy.mRotationTime < SecurityPolicy::kMinKeyRotationTime)
+ SecurityPolicy newPolicy = aSecurityPolicy;
+
+ if (newPolicy.mRotationTime < SecurityPolicy::kMinKeyRotationTime)
{
- LogNote("Key Rotation Time too small: %d", aSecurityPolicy.mRotationTime);
- ExitNow();
+ newPolicy.mRotationTime = SecurityPolicy::kMinKeyRotationTime;
+ LogNote("Key Rotation Time in SecurityPolicy is set to min allowed value of %u", newPolicy.mRotationTime);
}
- IgnoreError(Get<Notifier>().Update(mSecurityPolicy, aSecurityPolicy, kEventSecurityPolicyChanged));
+ if (newPolicy.mRotationTime != mSecurityPolicy.mRotationTime)
+ {
+ uint32_t newGuardTime = newPolicy.mRotationTime;
-exit:
- return;
+ // Calculations are done using a `uint32_t` variable to prevent
+ // potential overflow.
+
+ newGuardTime *= kKeySwitchGuardTimePercentage;
+ newGuardTime /= 100;
+
+ mKeySwitchGuardTime = static_cast<uint16_t>(newGuardTime);
+ }
+
+ IgnoreError(Get<Notifier>().Update(mSecurityPolicy, newPolicy, kEventSecurityPolicyChanged));
+
+ CheckForKeyRotation();
}
-void KeyManager::StartKeyRotationTimer(void)
+void KeyManager::ResetKeyRotationTimer(void)
{
mHoursSinceKeyRotation = 0;
- mKeyRotationTimer.Start(kOneHourIntervalInMsec);
+ mKeyRotationTimer.Start(Time::kOneHourInMsec);
}
void KeyManager::HandleKeyRotationTimer(void)
{
+ mKeyRotationTimer.Start(Time::kOneHourInMsec);
+
mHoursSinceKeyRotation++;
- // Order of operations below is important. We should restart the timer (from
- // last fire time for one hour interval) before potentially calling
- // `SetCurrentKeySequence()`. `SetCurrentKeySequence()` uses the fact that
- // timer is running to decide to check for the guard time and to reset the
- // rotation timer (and the `mHoursSinceKeyRotation`) if it updates the key
- // sequence.
+ if (mKeySwitchGuardTimer > 0)
+ {
+ mKeySwitchGuardTimer--;
+ }
- mKeyRotationTimer.StartAt(mKeyRotationTimer.GetFireTime(), kOneHourIntervalInMsec);
+ CheckForKeyRotation();
+}
+void KeyManager::CheckForKeyRotation(void)
+{
if (mHoursSinceKeyRotation >= mSecurityPolicy.mRotationTime)
{
- SetCurrentKeySequence(mKeySequence + 1);
+ SetCurrentKeySequence(mKeySequence + 1, kForceUpdate);
}
}
diff --git a/src/core/thread/key_manager.hpp b/src/core/thread/key_manager.hpp
index 18f11f2..099854c 100644
--- a/src/core/thread/key_manager.hpp
+++ b/src/core/thread/key_manager.hpp
@@ -77,8 +77,17 @@
*/
static constexpr uint8_t kVersionThresholdOffsetVersion = 3;
- static constexpr uint16_t kMinKeyRotationTime = 1; ///< The minimum Key Rotation Time in hours.
- static constexpr uint16_t kDefaultKeyRotationTime = 672; ///< Default Key Rotation Time (in unit of hours).
+ /**
+ * Default Key Rotation Time (in unit of hours).
+ *
+ */
+ static constexpr uint16_t kDefaultKeyRotationTime = 672;
+
+ /**
+ * Minimum Key Rotation Time (in unit of hours).
+ *
+ */
+ static constexpr uint16_t kMinKeyRotationTime = 2;
/**
* Initializes the object with default Key Rotation Time
@@ -212,6 +221,18 @@
{
public:
/**
+ * Determines whether to apply or ignore key switch guard when updating the key sequence.
+ *
+ * Used as input by `SetCurrentKeySequence()`.
+ *
+ */
+ enum KeySequenceUpdateMode : 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.
+ };
+
+ /**
* Initializes the object.
*
* @param[in] aInstance A reference to the OpenThread instance.
@@ -321,10 +342,14 @@
/**
* Sets the current key sequence value.
*
- * @param[in] aKeySequence The 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.
*
*/
- void SetCurrentKeySequence(uint32_t aKeySequence);
+ void SetCurrentKeySequence(uint32_t aKeySequence, KeySequenceUpdateMode aUpdateMode);
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
/**
@@ -500,17 +525,19 @@
* @returns The KeySwitchGuardTime value in hours.
*
*/
- uint32_t GetKeySwitchGuardTime(void) const { return mKeySwitchGuardTime; }
+ uint16_t GetKeySwitchGuardTime(void) const { return mKeySwitchGuardTime; }
/**
* Sets the KeySwitchGuardTime.
*
* The KeySwitchGuardTime is the time interval during which key rotation procedure is prevented.
*
- * @param[in] aKeySwitchGuardTime The KeySwitchGuardTime value in hours.
+ * Intended for testing only. Changing the guard time will render device non-compliant with the Thread spec.
+ *
+ * @param[in] aGuardTime The KeySwitchGuardTime value in hours.
*
*/
- void SetKeySwitchGuardTime(uint32_t aKeySwitchGuardTime) { mKeySwitchGuardTime = aKeySwitchGuardTime; }
+ void SetKeySwitchGuardTime(uint16_t aGuardTime) { mKeySwitchGuardTime = aGuardTime; }
/**
* Returns the Security Policy.
@@ -565,9 +592,13 @@
#endif
private:
- static constexpr uint32_t kDefaultKeySwitchGuardTime = 624;
- static constexpr uint32_t kOneHourIntervalInMsec = 3600u * 1000u;
- static constexpr bool kExportableMacKeys = OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE;
+ static constexpr uint16_t kDefaultKeySwitchGuardTime = 624; // ~ 93% of 672 (default key rotation time)
+ static constexpr uint32_t kKeySwitchGuardTimePercentage = 93; // Percentage of key rotation time.
+ static constexpr bool kExportableMacKeys = OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE;
+
+ static_assert(kDefaultKeySwitchGuardTime ==
+ SecurityPolicy::kDefaultKeyRotationTime * kKeySwitchGuardTimePercentage / 100,
+ "Default key switch guard time value is not correct");
OT_TOOL_PACKED_BEGIN
struct Keys
@@ -591,8 +622,9 @@
void ComputeTrelKey(uint32_t aKeySequence, Mac::Key &aKey) const;
#endif
- void StartKeyRotationTimer(void);
+ void ResetKeyRotationTimer(void);
void HandleKeyRotationTimer(void);
+ void CheckForKeyRotation(void);
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
void StoreNetworkKey(const NetworkKey &aNetworkKey, bool aOverWriteExisting);
@@ -630,9 +662,9 @@
uint32_t mStoredMacFrameCounter;
uint32_t mStoredMleFrameCounter;
- uint32_t mHoursSinceKeyRotation;
- uint32_t mKeySwitchGuardTime;
- bool mKeySwitchGuardEnabled;
+ uint16_t mHoursSinceKeyRotation;
+ uint16_t mKeySwitchGuardTime;
+ uint16_t mKeySwitchGuardTimer;
RotationTimer mKeyRotationTimer;
#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
diff --git a/src/core/thread/link_quality.cpp b/src/core/thread/link_quality.cpp
index bf6b35b..6747540 100644
--- a/src/core/thread/link_quality.cpp
+++ b/src/core/thread/link_quality.cpp
@@ -38,6 +38,7 @@
#include "common/code_utils.hpp"
#include "common/locator_getters.hpp"
#include "common/num_utils.hpp"
+#include "common/numeric_limits.hpp"
#include "instance/instance.hpp"
namespace ot {
@@ -116,16 +117,20 @@
void LqiAverager::Add(uint8_t aLqi)
{
- uint8_t count;
+ uint8_t count;
+ uint16_t newAverage;
- if (mCount < UINT8_MAX)
+ if (mCount < NumericLimits<uint8_t>::kMax)
{
mCount++;
}
count = Min(static_cast<uint8_t>(1 << kCoeffBitShift), mCount);
- mAverage = static_cast<uint8_t>(((mAverage * (count - 1)) + aLqi) / count);
+ newAverage = mAverage;
+ newAverage = (newAverage * (count - 1) + aLqi) / count;
+
+ mAverage = static_cast<uint8_t>(newAverage);
}
void LinkQualityInfo::Clear(void)
diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp
index df1aa25..4b5b4b2 100644
--- a/src/core/thread/mesh_forwarder.cpp
+++ b/src/core/thread/mesh_forwarder.cpp
@@ -1477,7 +1477,7 @@
message->SetDatagramTag(fragmentHeader.GetDatagramTag());
message->SetTimestampToNow();
- message->SetLinkInfo(aLinkInfo);
+ message->UpdateLinkInfoFrom(aLinkInfo);
VerifyOrExit(Get<Ip6::Filter>().Accept(*message), error = kErrorDrop);
@@ -1530,9 +1530,7 @@
message->WriteData(message->GetOffset(), aFrameData);
message->MoveOffset(aFrameData.GetLength());
message->AddRss(aLinkInfo.GetRss());
-#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
message->AddLqi(aLinkInfo.GetLqi());
-#endif
message->SetTimestampToNow();
}
@@ -1543,7 +1541,7 @@
if (message->GetOffset() >= message->GetLength())
{
mReassemblyList.Dequeue(*message);
- IgnoreError(HandleDatagram(*message, aLinkInfo, aMacAddrs.mSource));
+ IgnoreError(HandleDatagram(*message, aMacAddrs.mSource));
}
}
else
@@ -1643,7 +1641,7 @@
SuccessOrExit(error = FrameToMessage(aFrameData, 0, aMacAddrs, message));
- message->SetLinkInfo(aLinkInfo);
+ message->UpdateLinkInfoFrom(aLinkInfo);
VerifyOrExit(Get<Ip6::Filter>().Accept(*message), error = kErrorDrop);
@@ -1655,7 +1653,7 @@
if (error == kErrorNone)
{
- IgnoreError(HandleDatagram(*message, aLinkInfo, aMacAddrs.mSource));
+ IgnoreError(HandleDatagram(*message, aMacAddrs.mSource));
}
else
{
@@ -1664,7 +1662,7 @@
}
}
-Error MeshForwarder::HandleDatagram(Message &aMessage, const ThreadLinkInfo &aLinkInfo, const Mac::Address &aMacSource)
+Error MeshForwarder::HandleDatagram(Message &aMessage, const Mac::Address &aMacSource)
{
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Get<Utils::HistoryTracker>().RecordRxMessage(aMessage, aMacSource);
@@ -1680,7 +1678,7 @@
aMessage.SetLoopbackToHostAllowed(true);
aMessage.SetOrigin(Message::kOriginThreadNetif);
- return Get<Ip6::Ip6>().HandleDatagram(OwnedPtr<Message>(&aMessage), &aLinkInfo);
+ return Get<Ip6::Ip6>().HandleDatagram(OwnedPtr<Message>(&aMessage));
}
Error MeshForwarder::GetFramePriority(const FrameData &aFrameData,
diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp
index 8c3a790..1ccd0d0 100644
--- a/src/core/thread/mesh_forwarder.hpp
+++ b/src/core/thread/mesh_forwarder.hpp
@@ -550,7 +550,7 @@
uint16_t aFragmentLength,
uint16_t aSrcRloc16,
Message::Priority aPriority);
- Error HandleDatagram(Message &aMessage, const ThreadLinkInfo &aLinkInfo, const Mac::Address &aMacSource);
+ Error HandleDatagram(Message &aMessage, const Mac::Address &aMacSource);
void ClearReassemblyList(void);
void EvictMessage(Message &aMessage);
void HandleDiscoverComplete(void);
diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp
index e91ceb1..d4911b9 100644
--- a/src/core/thread/mesh_forwarder_ftd.cpp
+++ b/src/core/thread/mesh_forwarder_ftd.cpp
@@ -627,10 +627,16 @@
error = ip6Headers.DecompressFrom(aFrameData, aMeshAddrs, GetInstance());
- if (error == kErrorNotFound)
+ switch (error)
{
+ case kErrorNone:
+ break;
+ case kErrorNotFound:
// Frame may not contain an IPv6 header.
- ExitNow(error = kErrorNone);
+ error = kErrorNone;
+ OT_FALL_THROUGH;
+ default:
+ ExitNow();
}
error = Get<Mle::MleRouter>().CheckReachability(aMeshAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header());
@@ -706,13 +712,7 @@
SuccessOrExit(error = meshHeader.AppendTo(*messagePtr));
SuccessOrExit(error = messagePtr->AppendData(aFrameData));
- messagePtr->SetLinkInfo(aLinkInfo);
-
-#if OPENTHREAD_CONFIG_MULTI_RADIO
- // We set the received radio type on the message in order for it
- // to be logged correctly from LogMessage().
- messagePtr->SetRadioType(static_cast<Mac::RadioType>(aLinkInfo.mRadioType));
-#endif
+ messagePtr->UpdateLinkInfoFrom(aLinkInfo);
LogMessage(kMessageReceive, *messagePtr, kErrorNone, &aMacSource);
diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp
index 7eeb25d..99e6809 100644
--- a/src/core/thread/mle.cpp
+++ b/src/core/thread/mle.cpp
@@ -378,7 +378,7 @@
SuccessOrExit(Get<Settings>().Read(networkInfo));
- Get<KeyManager>().SetCurrentKeySequence(networkInfo.GetKeySequence());
+ Get<KeyManager>().SetCurrentKeySequence(networkInfo.GetKeySequence(), KeyManager::kForceUpdate);
Get<KeyManager>().SetMleFrameCounter(networkInfo.GetMleFrameCounter());
Get<KeyManager>().SetAllMacFrameCounters(networkInfo.GetMacFrameCounter(), /* aSetIfLarger */ false);
@@ -454,7 +454,8 @@
mWasLeader = networkInfo.GetRole() == kRoleLeader;
#endif
- // Successfully restored the network information from non-volatile settings after boot.
+ // Successfully restored the network information from
+ // non-volatile settings after boot.
mHasRestored = true;
exit:
@@ -533,7 +534,7 @@
VerifyOrExit(!IsDetached() || mAttachState != kAttachStateStart);
- // not in reattach stage after reset
+ // Not in reattach stage after reset
if (mReattachState == kReattachStop)
{
Get<MeshCoP::PendingDatasetManager>().HandleDetach();
@@ -1093,7 +1094,7 @@
{
aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(aNeighbor.GetExtAddress());
aNeighbor.GetLinkInfo().Clear();
- aNeighbor.GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss());
+ aNeighbor.GetLinkInfo().AddRss(aRxInfo.mMessage.GetAverageRss());
aNeighbor.ResetLinkFailures();
aNeighbor.SetLastHeard(TimerMilli::GetNow());
}
@@ -1120,7 +1121,6 @@
{
if (!Get<ThreadNetif>().HasUnicastAddress(mMeshLocal64.GetAddress()))
{
- // Mesh Local EID was removed, choose a new one and add it back
mMeshLocal64.GetAddress().GetIid().GenerateRandom();
Get<ThreadNetif>().AddUnicastAddress(mMeshLocal64);
@@ -1135,9 +1135,12 @@
if (aEvents.ContainsAny(kEventIp6MulticastSubscribed | kEventIp6MulticastUnsubscribed))
{
- // When multicast subscription changes, SED always notifies its parent as it depends on its
- // parent for indirect transmission. Since Thread 1.2, MED MAY also notify its parent of 1.2
- // or higher version as it could depend on its parent to perform Multicast Listener Report.
+ // When multicast subscription changes, SED always notifies
+ // its parent as it depends on its parent for indirect
+ // transmission. Since Thread 1.2, MED MAY also notify its
+ // parent of 1.2 or higher version as it could depend on its
+ // parent to perform Multicast Listener Report.
+
if (IsChild() && !IsFullThreadDevice() &&
(!IsRxOnWhenIdle()
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
@@ -1577,7 +1580,7 @@
}
else if (!IsRxOnWhenIdle())
{
- // return to sleepy operation
+ // Return to sleepy operation
Get<DataPollSender>().SetAttachMode(false);
Get<MeshForwarder>().SetRxOnWhenIdle(false);
}
@@ -1653,7 +1656,6 @@
exit:
if (error != kErrorNone)
{
- // do not use `FreeMessageOnError()` to avoid null check on nonnull pointer
aMessage.Free();
}
}
@@ -1782,9 +1784,13 @@
{
// Invalidate stale parent state.
//
- // Parent state is not normally invalidated after becoming a Router/Leader (see #1875). When trying to
- // attach to a better partition, invalidating old parent state (especially when in kStateRestored) ensures
- // that FindNeighbor() returns mParentCandidate when processing the Child ID Response.
+ // Parent state is not normally invalidated after becoming
+ // a Router/Leader (see #1875). When trying to attach to
+ // a better partition, invalidating old parent state
+ // (especially when in `kStateRestored`) ensures that
+ // `FindNeighbor()` returns `mParentCandidate` when
+ // processing the Child ID Response.
+
mParent.SetState(Neighbor::kStateInvalid);
}
}
@@ -1802,7 +1808,7 @@
{
SuccessOrExit(error = message->AppendAddressRegistrationTlv(mAddressRegistrationMode));
- // no need to request the last Route64 TLV for MTD
+ // No need to request the last Route64 TLV for MTD
tlvsLen -= 1;
}
@@ -2011,9 +2017,8 @@
case kChildUpdateRequestPending:
if (Get<Notifier>().IsPending())
{
- // Here intentionally delay another kChildUpdateRequestPendingDelay
- // cycle to ensure we only send a Child Update Request after we
- // know there are no more pending changes.
+ // Add another delay to ensures the Child Update Request is sent
+ // only after all pending changes are incorporated.
ScheduleMessageTransmissionTimer();
ExitNow();
}
@@ -2052,8 +2057,13 @@
ExitNow();
}
- mChildUpdateRequestState = kChildUpdateRequestActive;
- ScheduleMessageTransmissionTimer();
+ if (aMode != kAppendZeroTimeout)
+ {
+ // Enable MLE retransmissions on all Child Update Request
+ // messages, except when actively detaching.
+ mChildUpdateRequestState = kChildUpdateRequestActive;
+ ScheduleMessageTransmissionTimer();
+ }
VerifyOrExit((message = NewMleMessage(kCommandChildUpdateRequest)) != nullptr, error = kErrorNoBufs);
SuccessOrExit(error = message->AppendModeTlv(mDeviceMode));
@@ -2431,7 +2441,7 @@
LogDebg("Receive MLE message");
- VerifyOrExit(aMessageInfo.GetLinkInfo() != nullptr);
+ VerifyOrExit(aMessage.GetOrigin() == Message::kOriginThreadNetif);
VerifyOrExit(aMessageInfo.GetHopLimit() == kMleHopLimit, error = kErrorParse);
SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), securitySuite));
@@ -2642,13 +2652,13 @@
case kCommandChildIdRequest:
Get<MleRouter>().HandleChildIdRequest(rxInfo);
break;
+#endif // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
case kCommandTimeSync:
- Get<MleRouter>().HandleTimeSync(rxInfo);
+ HandleTimeSync(rxInfo);
break;
#endif
-#endif // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
case kCommandLinkMetricsManagementRequest:
@@ -2699,7 +2709,7 @@
// We skip logging failures for broadcast MLE messages since it
// can be common to receive such messages from adjacent Thread
// networks.
- if (!aMessageInfo.GetSockAddr().IsMulticast() || !aMessageInfo.GetThreadLinkInfo()->IsDstPanIdBroadcast())
+ if (!aMessageInfo.GetSockAddr().IsMulticast() || !aMessage.IsDstPanIdBroadcast())
{
LogProcessError(kTypeGenericUdp, error);
}
@@ -2726,7 +2736,7 @@
switch (aRxInfo.mClass)
{
case RxInfo::kAuthoritativeMessage:
- Get<KeyManager>().SetCurrentKeySequence(aRxInfo.mKeySequence);
+ Get<KeyManager>().SetCurrentKeySequence(aRxInfo.mKeySequence, KeyManager::kForceUpdate);
break;
case RxInfo::kPeerMessage:
@@ -2734,7 +2744,7 @@
{
if (aRxInfo.mKeySequence - Get<KeyManager>().GetCurrentKeySequence() == 1)
{
- Get<KeyManager>().SetCurrentKeySequence(aRxInfo.mKeySequence);
+ Get<KeyManager>().SetCurrentKeySequence(aRxInfo.mKeySequence, KeyManager::kApplyKeySwitchGuard);
}
else
{
@@ -2875,11 +2885,8 @@
if (mDataRequestState == kDataRequestNone && !IsRxOnWhenIdle())
{
- // Here simply stops fast data poll request by Mle Data Request.
- // Note that in some cases fast data poll may continue after below stop operation until
- // running out the specified number. E.g. other component also trigger fast poll, and
- // is waiting for response; or the corner case where multiple Mle Data Request attempts
- // happened due to the retransmission mechanism.
+ // Stop fast data poll request by MLE since we received
+ // the response.
Get<DataPollSender>().StopFastPolls();
}
@@ -2913,7 +2920,6 @@
uint16_t pendingDatasetLength = 0;
bool dataRequest = false;
- // Leader Data
SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
if ((leaderData.GetPartitionId() != mLeaderData.GetPartitionId()) ||
@@ -2934,7 +2940,6 @@
VerifyOrExit(IsNetworkDataNewer(leaderData));
}
- // Active Timestamp
switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, activeTimestamp))
{
case kErrorNone:
@@ -2942,8 +2947,9 @@
timestamp = Get<MeshCoP::ActiveDatasetManager>().GetTimestamp();
- // if received timestamp does not match the local value and message does not contain the dataset,
- // send MLE Data Request
+ // Send an MLE Data Request if the received timestamp
+ // mismatches the local value and the message does not
+ // include the dataset.
if (!IsLeader() && (MeshCoP::Timestamp::Compare(&activeTimestamp, timestamp) != 0) &&
(Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kActiveDataset, activeDatasetOffset, activeDatasetLength) !=
kErrorNone))
@@ -2960,7 +2966,6 @@
ExitNow(error = kErrorParse);
}
- // Pending Timestamp
switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, pendingTimestamp))
{
case kErrorNone:
@@ -2968,8 +2973,6 @@
timestamp = Get<MeshCoP::PendingDatasetManager>().GetTimestamp();
- // if received timestamp does not match the local value and message does not contain the dataset,
- // send MLE Data Request
if (!IsLeader() && (MeshCoP::Timestamp::Compare(&pendingTimestamp, timestamp) != 0) &&
(Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kPendingDataset, pendingDatasetOffset,
pendingDatasetLength) != kErrorNone))
@@ -3007,7 +3010,6 @@
else
#endif
{
- // Active Dataset
if (hasActiveTimestamp)
{
if (activeDatasetOffset > 0)
@@ -3017,7 +3019,6 @@
}
}
- // Pending Dataset
if (hasPendingTimestamp)
{
if (pendingDatasetOffset > 0)
@@ -3132,7 +3133,7 @@
void Mle::HandleParentResponse(RxInfo &aRxInfo)
{
Error error = kErrorNone;
- int8_t rss = aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss();
+ int8_t rss = aRxInfo.mMessage.GetAverageRss();
RxChallenge response;
uint16_t version;
uint16_t sourceAddress;
@@ -3149,16 +3150,13 @@
TimeParameterTlv timeParameterTlv;
#endif
- // Source Address
SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
Log(kMessageReceive, kTypeParentResponse, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress);
- // Version
SuccessOrExit(error = Tlv::Find<VersionTlv>(aRxInfo.mMessage, version));
VerifyOrExit(version >= kThreadVersion1p1, error = kErrorParse);
- // Response
SuccessOrExit(error = aRxInfo.mMessage.ReadResponseTlv(response));
VerifyOrExit(response == mParentRequestChallenge, error = kErrorParse);
@@ -3169,20 +3167,16 @@
mReceivedResponseFromParent = true;
}
- // Leader Data
SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
- // Link Margin
SuccessOrExit(error = Tlv::Find<LinkMarginTlv>(aRxInfo.mMessage, linkMarginFromTlv));
linkMargin = Min(Get<Mac::Mac>().ComputeLinkMargin(rss), linkMarginFromTlv);
linkQuality = LinkQualityForLinkMargin(linkMargin);
- // Connectivity
SuccessOrExit(error = Tlv::FindTlv(aRxInfo.mMessage, connectivityTlv));
VerifyOrExit(connectivityTlv.IsValid(), error = kErrorParse);
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
- // CSL Accuracy
switch (aRxInfo.mMessage.ReadCslClockAccuracyTlv(cslAccuracy))
{
case kErrorNone:
@@ -3255,7 +3249,7 @@
if (mParentCandidate.IsStateParentResponse() && (mParentCandidate.GetExtAddress() != extAddress))
{
- // if already have a candidate parent, only seek a better parent
+ // If already have a candidate parent, only seek a better parent
int compare = 0;
@@ -3266,23 +3260,21 @@
mParentCandidate.mIsSingleton, mParentCandidate.mLeaderData);
}
- // only consider partitions that are the same or better
+ // Only consider partitions that are the same or better
VerifyOrExit(compare >= 0);
#endif
- // only consider better parents if the partitions are the same
+ // Only consider better parents if the partitions are the same
if (compare == 0)
{
VerifyOrExit(IsBetterParent(sourceAddress, linkQuality, linkMargin, connectivityTlv, version, cslAccuracy));
}
}
- // Link/MLE Frame Counters
SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter));
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
- // Time Parameter
if (Tlv::FindTlv(aRxInfo.mMessage, timeParameterTlv) == kErrorNone)
{
VerifyOrExit(timeParameterTlv.IsValid());
@@ -3294,14 +3286,13 @@
#if OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED
else
{
- // If the time sync feature is required, don't choose the parent which doesn't support it.
+ // If the time sync feature is required, don't choose the
+ // parent which doesn't support it.
ExitNow();
}
-
-#endif // OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED
+#endif
#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
- // Challenge
SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(mParentCandidate.mRxChallenge));
InitNeighbor(mParentCandidate, aRxInfo);
@@ -3346,7 +3337,6 @@
uint16_t offset;
uint16_t length;
- // Source Address
SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
Log(kMessageReceive, kTypeChildIdResponse, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress);
@@ -3355,22 +3345,17 @@
VerifyOrExit(mAttachState == kAttachStateChildIdRequest);
- // ShortAddress
SuccessOrExit(error = Tlv::Find<Address16Tlv>(aRxInfo.mMessage, shortAddress));
VerifyOrExit(RouterIdMatch(sourceAddress, shortAddress), error = kErrorRejected);
- // Leader Data
SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
- // Network Data
SuccessOrExit(
error = Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kNetworkData, networkDataOffset, networkDataLength));
- // Active Timestamp
switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, timestamp))
{
case kErrorNone:
- // Active Dataset
if (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kActiveDataset, offset, length) == kErrorNone)
{
SuccessOrExit(error =
@@ -3385,17 +3370,15 @@
ExitNow(error = kErrorParse);
}
- // clear Pending Dataset if device succeed to reattach using stored Pending Dataset
+ // Clear Pending Dataset if device succeed to reattach using stored Pending Dataset
if (mReattachState == kReattachPending)
{
Get<MeshCoP::PendingDatasetManager>().Clear();
}
- // Pending Timestamp
switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, timestamp))
{
case kErrorNone:
- // Pending Dataset
if (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kPendingDataset, offset, length) == kErrorNone)
{
IgnoreError(Get<MeshCoP::PendingDatasetManager>().Save(timestamp, aRxInfo.mMessage, offset, length));
@@ -3411,7 +3394,6 @@
}
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
- // Sync to Thread network time
if (aRxInfo.mMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ)
{
Get<TimeSync>().HandleTimeSyncMessage(aRxInfo.mMessage);
@@ -3467,12 +3449,10 @@
TlvList requestedTlvList;
TlvList tlvList;
- // Source Address
SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
Log(kMessageReceive, kTypeChildUpdateRequestAsChild, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress);
- // Challenge
switch (aRxInfo.mMessage.ReadChallengeTlv(challenge))
{
case kErrorNone:
@@ -3508,7 +3488,6 @@
ExitNow();
}
- // Leader Data, Network Data, Active Timestamp, Pending Timestamp
SuccessOrExit(error = HandleLeaderData(aRxInfo));
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
@@ -3517,7 +3496,8 @@
if (aRxInfo.mMessage.ReadCslClockAccuracyTlv(cslAccuracy) == kErrorNone)
{
- // MUST include CSL timeout TLV when request includes CSL accuracy
+ // MUST include CSL timeout TLV when request includes
+ // CSL accuracy
tlvList.Add(Tlv::kCslTimeout);
}
}
@@ -3525,11 +3505,10 @@
}
else
{
- // this device is not a child of the Child Update Request source
+ // This device is not a child of the Child Update Request source
tlvList.Add(Tlv::kStatus);
}
- // TLV Request
switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList))
{
case kErrorNone:
@@ -3551,7 +3530,8 @@
}
#endif
- // Send the response to the requester, regardless if it's this device's parent or not
+ // Send the response to the requester, regardless if it's this
+ // device's parent or not.
SuccessOrExit(error = SendChildUpdateResponse(tlvList, challenge, aRxInfo.mMessageInfo.GetPeerAddr()));
exit:
@@ -3596,14 +3576,12 @@
OT_ASSERT(false);
}
- // Status
if (Tlv::Find<StatusTlv>(aRxInfo.mMessage, status) == kErrorNone)
{
IgnoreError(BecomeDetached());
ExitNow();
}
- // Mode
SuccessOrExit(error = Tlv::Find<ModeTlv>(aRxInfo.mMessage, mode));
VerifyOrExit(DeviceMode(mode) == mDeviceMode, error = kErrorDrop);
@@ -3631,7 +3609,6 @@
OT_FALL_THROUGH;
case kRoleChild:
- // Source Address
SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
if (RouterIdFromRloc16(sourceAddress) != RouterIdFromRloc16(GetRloc16()))
@@ -3640,10 +3617,8 @@
ExitNow();
}
- // Leader Data, Network Data, Active Timestamp, Pending Timestamp
SuccessOrExit(error = HandleLeaderData(aRxInfo));
- // Timeout optional
switch (Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout))
{
case kErrorNone:
@@ -3666,7 +3641,6 @@
{
Mac::CslAccuracy cslAccuracy;
- // CSL Accuracy
switch (aRxInfo.mMessage.ReadCslClockAccuracyTlv(cslAccuracy))
{
case kErrorNone:
@@ -3815,8 +3789,23 @@
exit:
LogProcessError(kTypeLinkMetricsManagementRequest, error);
}
+#endif
-#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+void Mle::HandleTimeSync(RxInfo &aRxInfo)
+{
+ Log(kMessageReceive, kTypeTimeSync, aRxInfo.mMessageInfo.GetPeerAddr());
+
+ VerifyOrExit(aRxInfo.IsNeighborStateValid());
+
+ aRxInfo.mClass = RxInfo::kPeerMessage;
+
+ Get<TimeSync>().HandleTimeSyncMessage(aRxInfo.mMessage);
+
+exit:
+ return;
+}
+#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
void Mle::HandleLinkMetricsManagementResponse(RxInfo &aRxInfo)
@@ -3835,7 +3824,7 @@
exit:
LogProcessError(kTypeLinkMetricsManagementResponse, error);
}
-#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
+#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
void Mle::HandleLinkProbe(RxInfo &aRxInfo)
@@ -3856,7 +3845,7 @@
exit:
LogProcessError(kTypeLinkProbe, error);
}
-#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
+#endif
void Mle::ProcessAnnounce(void)
{
@@ -3916,22 +3905,6 @@
bool Mle::IsMeshLocalAddress(const Ip6::Address &aAddress) const { return (aAddress.GetPrefix() == mMeshLocalPrefix); }
-Error Mle::CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header)
-{
- Error error;
-
- if ((aMeshDest != GetRloc16()) || Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination()))
- {
- error = kErrorNone;
- }
- else
- {
- error = kErrorNoRoute;
- }
-
- return error;
-}
-
#if OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH
void Mle::InformPreviousParent(void)
{
@@ -3951,13 +3924,8 @@
LogNote("Sending message to inform previous parent 0x%04x", mPreviousParentRloc);
exit:
-
- if (error != kErrorNone)
- {
- LogWarn("Failed to inform previous parent: %s", ErrorToString(error));
-
- FreeMessage(message);
- }
+ LogWarnOnError(error, "inform previous parent");
+ FreeMessageOnError(message, error);
}
#endif // OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH
@@ -4158,14 +4126,14 @@
"Link Reject", // (25) kTypeLinkReject
"Link Request", // (26) kTypeLinkRequest
"Parent Request", // (27) kTypeParentRequest
-#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
- "Time Sync", // (28) kTypeTimeSync
-#endif
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
- "Link Metrics Management Request", // (29) kTypeLinkMetricsManagementRequest
- "Link Metrics Management Response", // (30) kTypeLinkMetricsManagementResponse
- "Link Probe", // (31) kTypeLinkProbe
+ "Link Metrics Management Request", // (28) kTypeLinkMetricsManagementRequest
+ "Link Metrics Management Response", // (29) kTypeLinkMetricsManagementResponse
+ "Link Probe", // (30) kTypeLinkProbe
+#endif
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+ "Time Sync", // (31) kTypeTimeSync
#endif
};
@@ -4198,25 +4166,30 @@
static_assert(kTypeLinkReject == 25, "kTypeLinkReject value is incorrect");
static_assert(kTypeLinkRequest == 26, "kTypeLinkRequest value is incorrect");
static_assert(kTypeParentRequest == 27, "kTypeParentRequest value is incorrect");
-#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
- static_assert(kTypeTimeSync == 28, "kTypeTimeSync value is incorrect");
-#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
- static_assert(kTypeLinkMetricsManagementRequest == 29, "kTypeLinkMetricsManagementRequest value is incorrect)");
- static_assert(kTypeLinkMetricsManagementResponse == 30, "kTypeLinkMetricsManagementResponse value is incorrect)");
- static_assert(kTypeLinkProbe == 31, "kTypeLinkProbe value is incorrect)");
-#endif
-#else // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
static_assert(kTypeLinkMetricsManagementRequest == 28, "kTypeLinkMetricsManagementRequest value is incorrect)");
static_assert(kTypeLinkMetricsManagementResponse == 29, "kTypeLinkMetricsManagementResponse value is incorrect)");
static_assert(kTypeLinkProbe == 30, "kTypeLinkProbe value is incorrect)");
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+ static_assert(kTypeTimeSync == 31, "kTypeTimeSync value is incorrect");
#endif
-#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-#else // OPENTHREAD_FTD
+#else
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+ static_assert(kTypeTimeSync == 28, "kTypeTimeSync value is incorrect");
+#endif
+#endif
+#else // OPENTHREAD_FTD
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
static_assert(kTypeLinkMetricsManagementRequest == 16, "kTypeLinkMetricsManagementRequest value is incorrect)");
static_assert(kTypeLinkMetricsManagementResponse == 17, "kTypeLinkMetricsManagementResponse value is incorrect)");
static_assert(kTypeLinkProbe == 18, "kTypeLinkProbe value is incorrect)");
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+ static_assert(kTypeTimeSync == 19, "kTypeTimeSync value is incorrect");
+#endif
+#else
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+ static_assert(kTypeTimeSync == 16, "kTypeTimeSync value is incorrect");
+#endif
#endif
#endif // OPENTHREAD_FTD
diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp
index 33d0920..873e338 100644
--- a/src/core/thread/mle.hpp
+++ b/src/core/thread/mle.hpp
@@ -973,15 +973,15 @@
kTypeLinkReject,
kTypeLinkRequest,
kTypeParentRequest,
-#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
- kTypeTimeSync,
-#endif
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
kTypeLinkMetricsManagementRequest,
kTypeLinkMetricsManagementResponse,
kTypeLinkProbe,
#endif
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+ kTypeTimeSync,
+#endif
};
//------------------------------------------------------------------------------------------------------------------
@@ -1240,7 +1240,6 @@
void SetAttachState(AttachState aState);
void InitNeighbor(Neighbor &aNeighbor, const RxInfo &aRxInfo);
void ClearParentCandidate(void) { mParentCandidate.Clear(); }
- Error CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header);
Error SendDataRequest(const Ip6::Address &aDestination);
void HandleNotifierEvents(Events aEvents);
void SendDelayedResponse(TxMessage &aMessage, const DelayedResponseMetadata &aMetadata);
@@ -1314,6 +1313,10 @@
void UpdateServiceAlocs(void);
#endif
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+ void HandleTimeSync(RxInfo &aRxInfo);
+#endif
+
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
void HandleLinkMetricsManagementRequest(RxInfo &aRxInfo);
void HandleLinkProbe(RxInfo &aRxInfo);
diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp
index 76b8dc3..b55341f 100644
--- a/src/core/thread/mle_router.cpp
+++ b/src/core/thread/mle_router.cpp
@@ -363,15 +363,22 @@
case kAnyPartition:
case kBetterParent:
- // If attach was started due to receiving MLE Announce Messages, all rx-on-when-idle devices would
- // start attach immediately when receiving such Announce message as in Thread 1.1 specification,
- // Section 4.8.1,
- // "If the received value is newer and the channel and/or PAN ID in the Announce message differ
- // from those currently in use, the receiving device attempts to attach using the channel and
- // PAN ID received from the Announce message."
+
+ // If attach was initiated due to receiving an MLE Announce
+ // message, all rx-on-when-idle devices will immediately
+ // attempt to attach as well. This aligns with the Thread 1.1
+ // specification (Section 4.8.1):
//
- // That is, Parent-child relationship is highly unlikely to be kept in the new partition, so here
- // removes all children, leaving whether to become router according to the new partition status.
+ // "If the received value is newer and the channel and/or PAN
+ // ID in the Announce message differ from those currently in
+ // use, the receiving device attempts to attach using the
+ // channel and PAN ID received from the Announce message."
+ //
+ // Since parent-child relationships are unlikely to persist in
+ // the new partition, we remove all children here. The
+ // decision to become router is determined based on the new
+ // partition's status.
+
if (IsAnnounceAttach() && HasChildren())
{
RemoveChildren();
@@ -442,7 +449,7 @@
Get<AddressResolver>().Clear();
}
- // Remove children that do not have matching RLOC16
+ // Remove children that do not have a matching RLOC16
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateValidOrRestoring))
{
if (RouterIdFromRloc16(child.GetRloc16()) != mRouterId)
@@ -521,17 +528,19 @@
Ip6::Address destination;
TxMessage *message = nullptr;
- // Suppress MLE Advertisements when trying to attach to a better partition.
- //
- // Without this suppression, a device may send an MLE Advertisement before receiving the MLE Child ID Response.
- // The candidate parent then removes the attaching device because the Source Address TLV includes an RLOC16 that
- // indicates a Router role (i.e. a Child ID equal to zero).
+ // Suppress MLE Advertisements when trying to attach to a better
+ // partition. Without this, a candidate parent might incorrectly
+ // interpret this advertisement (Source Address TLV containing an
+ // RLOC16 indicating device is acting as router) and reject the
+ // attaching device.
+
VerifyOrExit(!IsAttaching());
- // Suppress MLE Advertisements when transitioning to the router role.
- //
- // When trying to attach to a new partition, sending out advertisements as a REED can cause already-attached
- // children to detach.
+ // Suppress MLE Advertisements when attempting to transition to
+ // router role. Advertisements as a REED while attaching to a new
+ // partition can cause existing children to detach
+ // unnecessarily.
+
VerifyOrExit(!mAddressSolicitPending);
VerifyOrExit((message = NewMleMessage(kCommandAdvertisement)) != nullptr, error = kErrorNoBufs);
@@ -665,14 +674,11 @@
VerifyOrExit(!IsAttaching(), error = kErrorInvalidState);
- // Challenge
SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(challenge));
- // Version
SuccessOrExit(error = Tlv::Find<VersionTlv>(aRxInfo.mMessage, version));
VerifyOrExit(version >= kThreadVersion1p1, error = kErrorParse);
- // Leader Data
switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData))
{
case kErrorNone:
@@ -684,7 +690,6 @@
ExitNow(error = kErrorParse);
}
- // Source Address
switch (Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress))
{
case kErrorNone:
@@ -711,7 +716,8 @@
break;
case kErrorNotFound:
- // lack of source address indicates router coming out of reset
+ // A missing source address indicates that the router was
+ // recently reset.
VerifyOrExit(aRxInfo.IsNeighborStateValid() && IsActiveRouter(aRxInfo.mNeighbor->GetRloc16()),
error = kErrorDrop);
neighbor = aRxInfo.mNeighbor;
@@ -721,7 +727,6 @@
ExitNow(error = kErrorParse);
}
- // TLV Request
switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList))
{
case kErrorNone:
@@ -748,16 +753,16 @@
aRxInfo.mClass = RxInfo::kPeerMessage;
ProcessKeySequence(aRxInfo);
- SuccessOrExit(error = SendLinkAccept(aRxInfo.mMessageInfo, neighbor, requestedTlvList, challenge));
+ SuccessOrExit(error = SendLinkAccept(aRxInfo, neighbor, requestedTlvList, challenge));
exit:
LogProcessError(kTypeLinkRequest, error);
}
-Error MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo,
- Neighbor *aNeighbor,
- const TlvList &aRequestedTlvList,
- const RxChallenge &aChallenge)
+Error MleRouter::SendLinkAccept(const RxInfo &aRxInfo,
+ Neighbor *aNeighbor,
+ const TlvList &aRequestedTlvList,
+ const RxChallenge &aChallenge)
{
static const uint8_t kRouterTlvs[] = {Tlv::kLinkMargin};
@@ -775,9 +780,7 @@
SuccessOrExit(error = message->AppendLinkFrameCounterTlv());
SuccessOrExit(error = message->AppendMleFrameCounterTlv());
- // always append a link margin, regardless of whether or not it was requested
- linkMargin = Get<Mac::Mac>().ComputeLinkMargin(aMessageInfo.GetThreadLinkInfo()->GetRss());
-
+ linkMargin = Get<Mac::Mac>().ComputeLinkMargin(aRxInfo.mMessage.GetAverageRss());
SuccessOrExit(error = message->AppendLinkMarginTlv(linkMargin));
if (aNeighbor != nullptr && IsActiveRouter(aNeighbor->GetRloc16()))
@@ -823,20 +826,20 @@
}
#endif
- if (aMessageInfo.GetSockAddr().IsMulticast())
+ if (aRxInfo.mMessageInfo.GetSockAddr().IsMulticast())
{
- SuccessOrExit(error = message->SendAfterDelay(aMessageInfo.GetPeerAddr(),
+ SuccessOrExit(error = message->SendAfterDelay(aRxInfo.mMessageInfo.GetPeerAddr(),
1 + Random::NonCrypto::GetUint16InRange(0, kMaxLinkAcceptDelay)));
Log(kMessageDelay, (command == kCommandLinkAccept) ? kTypeLinkAccept : kTypeLinkAcceptAndRequest,
- aMessageInfo.GetPeerAddr());
+ aRxInfo.mMessageInfo.GetPeerAddr());
}
else
{
- SuccessOrExit(error = message->SendTo(aMessageInfo.GetPeerAddr()));
+ SuccessOrExit(error = message->SendTo(aRxInfo.mMessageInfo.GetPeerAddr()));
Log(kMessageSend, (command == kCommandLinkAccept) ? kTypeLinkAccept : kTypeLinkAcceptAndRequest,
- aMessageInfo.GetPeerAddr());
+ aRxInfo.mMessageInfo.GetPeerAddr());
}
exit:
@@ -874,7 +877,6 @@
LeaderData leaderData;
uint8_t linkMargin;
- // Source Address
SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
Log(kMessageReceive, aRequest ? kTypeLinkAcceptAndRequest : kTypeLinkAccept, aRxInfo.mMessageInfo.GetPeerAddr(),
@@ -886,10 +888,8 @@
router = mRouterTable.FindRouterById(routerId);
neighborState = (router != nullptr) ? router->GetState() : Neighbor::kStateInvalid;
- // Response
SuccessOrExit(error = aRxInfo.mMessage.ReadResponseTlv(response));
- // verify response
switch (neighborState)
{
case Neighbor::kStateLinkRequest:
@@ -915,22 +915,20 @@
RemoveNeighbor(*aRxInfo.mNeighbor);
}
- // Version
SuccessOrExit(error = Tlv::Find<VersionTlv>(aRxInfo.mMessage, version));
VerifyOrExit(version >= kThreadVersion1p1, error = kErrorParse);
- // Link and MLE Frame Counters
SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter));
- // Link Margin
switch (Tlv::Find<LinkMarginTlv>(aRxInfo.mMessage, linkMargin))
{
case kErrorNone:
break;
case kErrorNotFound:
- // Link Margin TLV may be skipped in Router Synchronization process after Reset
+ // The Link Margin TLV may be omitted after a reset. We wait
+ // for MLE Advertisements to establish the routing cost to
+ // the neighbor
VerifyOrExit(IsDetached(), error = kErrorNotFound);
- // Wait for an MLE Advertisement to establish a routing cost to the neighbor
linkMargin = 0;
break;
default:
@@ -940,15 +938,12 @@
switch (mRole)
{
case kRoleDetached:
- // Address16
SuccessOrExit(error = Tlv::Find<Address16Tlv>(aRxInfo.mMessage, address16));
VerifyOrExit(GetRloc16() == address16, error = kErrorDrop);
- // Leader Data
SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId());
- // Route
mRouterTable.Clear();
SuccessOrExit(error = aRxInfo.mMessage.ReadRouteTlv(routeTlv));
SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo));
@@ -964,7 +959,7 @@
SetStateRouter(GetRloc16());
}
- mLinkRequestAttempts = 0; // completed router sync after reset, no more link request to retransmit
+ mLinkRequestAttempts = 0;
mRetrieveNewNetworkData = true;
IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr()));
@@ -981,7 +976,6 @@
case kRoleLeader:
VerifyOrExit(router != nullptr);
- // Leader Data
SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
VerifyOrExit(leaderData.GetPartitionId() == mLeaderData.GetPartitionId());
@@ -992,7 +986,6 @@
IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr()));
}
- // Route (optional)
switch (aRxInfo.mMessage.ReadRouteTlv(routeTlv))
{
case kErrorNone:
@@ -1026,7 +1019,6 @@
OT_ASSERT(false);
}
- // finish link synchronization
InitNeighbor(*router, aRxInfo);
router->SetRloc16(sourceAddress);
router->GetLinkFrameCounters().SetAll(linkFrameCounter);
@@ -1049,10 +1041,8 @@
RxChallenge challenge;
TlvList requestedTlvList;
- // Challenge
SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(challenge));
- // TLV Request
switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList))
{
case kErrorNone:
@@ -1062,7 +1052,7 @@
ExitNow(error = kErrorParse);
}
- SuccessOrExit(error = SendLinkAccept(aRxInfo.mMessageInfo, router, requestedTlvList, challenge));
+ SuccessOrExit(error = SendLinkAccept(aRxInfo, router, requestedTlvList, challenge));
}
exit:
@@ -1178,7 +1168,7 @@
// - `aLeaderData` is the read value from `LeaderDataTlv`.
Error error = kErrorNone;
- uint8_t linkMargin = Get<Mac::Mac>().ComputeLinkMargin(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss());
+ uint8_t linkMargin = Get<Mac::Mac>().ComputeLinkMargin(aRxInfo.mMessage.GetAverageRss());
RouteTlv routeTlv;
Router *router;
uint8_t routerId;
@@ -1218,7 +1208,7 @@
if (ComparePartitions(routeTlv.IsSingleton(), aLeaderData, IsSingleton(), mLeaderData) > 0
#if OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED
- // if time sync is required, it will only migrate to a better network which also enables time sync.
+ // Allow a better partition if it also enables time sync.
&& aRxInfo.mMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ
#endif
)
@@ -1333,7 +1323,9 @@
Get<AddressResolver>().ReplaceEntriesForRloc16(aRxInfo.mNeighbor->GetRloc16(), router->GetRloc16());
}
- // Send unicast link request if no link to router and no unicast/multicast link request in progress
+ // Send unicast link request if no link to router and no
+ // unicast/multicast link request in progress
+
if (!router->IsStateValid() && !router->IsStateLinkRequest() && (mChallengeTimeout == 0) &&
(linkMargin >= kLinkRequestMinMargin))
{
@@ -1350,7 +1342,6 @@
exit:
if (aRxInfo.mNeighbor && aRxInfo.mNeighbor->GetRloc16() != aSourceAddress)
{
- // Remove stale neighbors
RemoveNeighbor(*aRxInfo.mNeighbor);
}
@@ -1395,11 +1386,9 @@
aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
- // Version
SuccessOrExit(error = Tlv::Find<VersionTlv>(aRxInfo.mMessage, version));
VerifyOrExit(version >= kThreadVersion1p1, error = kErrorParse);
- // Scan Mask
SuccessOrExit(error = Tlv::Find<ScanMaskTlv>(aRxInfo.mMessage, scanMask));
switch (mRole)
@@ -1419,7 +1408,6 @@
break;
}
- // Challenge
SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(challenge));
child = mChildTable.FindChild(extAddr, Child::kInStateAnyExceptInvalid);
@@ -1428,7 +1416,6 @@
{
VerifyOrExit((child = mChildTable.GetNewChild()) != nullptr, error = kErrorNoBufs);
- // MAC Address
InitNeighbor(*child, aRxInfo);
child->SetState(Neighbor::kStateParentRequest);
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
@@ -1510,6 +1497,9 @@
mPreviousPartitionIdTimeout--;
}
+ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // Role transitions
+
roleTransitionTimeoutExpired = mRouterRoleTransition.HandleTimeTick();
switch (mRole)
@@ -1532,7 +1522,6 @@
}
else
{
- // send announce after decided to stay in REED if needed
InformPreviousChannel();
}
@@ -1549,11 +1538,11 @@
OT_FALL_THROUGH;
case kRoleRouter:
- LogDebg("network id timeout = %lu", ToUlong(mRouterTable.GetLeaderAge()));
+ LogDebg("Leader age %lu", ToUlong(mRouterTable.GetLeaderAge()));
if ((mRouterTable.GetActiveRouterCount() > 0) && (mRouterTable.GetLeaderAge() >= mNetworkIdTimeout))
{
- LogInfo("Router ID Sequence timeout");
+ LogInfo("Leader age timeout");
Attach(kSamePartition);
}
@@ -1578,7 +1567,9 @@
OT_ASSERT(false);
}
- // update children state
+ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // Update `ChildTable`
+
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
{
uint32_t timeout = 0;
@@ -1605,7 +1596,7 @@
if (child.IsCslSynchronized() &&
TimerMilli::GetNow() - child.GetCslLastHeard() >= Time::SecToMsec(child.GetCslTimeout()))
{
- LogInfo("Child CSL synchronization expired");
+ LogInfo("Child 0x%04x CSL synchronization expired", child.GetRloc16());
child.SetCslSynchronized(false);
Get<CslTxScheduler>().Update();
}
@@ -1613,7 +1604,7 @@
if (TimerMilli::GetNow() - child.GetLastHeard() >= timeout)
{
- LogInfo("Child timeout expired");
+ LogInfo("Child 0x%04x timeout expired", child.GetRloc16());
RemoveNeighbor(child);
}
else if (IsRouterOrLeader() && child.IsStateRestored())
@@ -1622,7 +1613,9 @@
}
}
- // update router state
+ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // Update `RouterTable`
+
for (Router &router : Get<RouterTable>())
{
uint32_t age;
@@ -1635,53 +1628,35 @@
age = TimerMilli::GetNow() - router.GetLastHeard();
- if (router.IsStateValid())
+ if (router.IsStateValid() && (age >= kMaxNeighborAge))
{
-#if OPENTHREAD_CONFIG_MLE_SEND_LINK_REQUEST_ON_ADV_TIMEOUT == 0
-
- if (age >= kMaxNeighborAge)
+#if OPENTHREAD_CONFIG_MLE_SEND_LINK_REQUEST_ON_ADV_TIMEOUT
+ if (age < kMaxNeighborAge + kMaxTxCount * kUnicastRetxDelay)
{
- LogInfo("Router timeout expired");
- RemoveNeighbor(router);
- continue;
+ LogInfo("No Adv from router 0x%04x - sending Link Request", router.GetRloc16());
+ IgnoreError(SendLinkRequest(&router));
}
-
-#else
-
- if (age >= kMaxNeighborAge)
- {
- if (age < kMaxNeighborAge + kMaxTxCount * kUnicastRetxDelay)
- {
- LogInfo("Router timeout expired");
- IgnoreError(SendLinkRequest(&router));
- }
- else
- {
- RemoveNeighbor(router);
- continue;
- }
- }
-
+ else
#endif
- }
- else if (router.IsStateLinkRequest())
- {
- if (age >= kLinkRequestTimeout)
{
- LogInfo("Link Request timeout expired");
+ LogInfo("Router 0x%04x timeout expired", router.GetRloc16());
RemoveNeighbor(router);
continue;
}
}
- if (IsLeader())
+ if (router.IsStateLinkRequest() && (age >= kLinkRequestTimeout))
{
- if (mRouterTable.FindNextHopOf(router) == nullptr && mRouterTable.GetLinkCost(router) >= kMaxRouteCost &&
- age >= kMaxLeaderToRouterTimeout)
- {
- LogInfo("Router ID timeout expired (no route)");
- IgnoreError(mRouterTable.Release(router.GetRouterId()));
- }
+ LogInfo("Router 0x%04x - Link Request timeout expired", router.GetRloc16());
+ RemoveNeighbor(router);
+ continue;
+ }
+
+ if (IsLeader() && (mRouterTable.FindNextHopOf(router) == nullptr) &&
+ (mRouterTable.GetLinkCost(router) >= kMaxRouteCost) && (age >= kMaxLeaderToRouterTimeout))
+ {
+ LogInfo("Router 0x%04x ID timeout expired (no route)", router.GetRloc16());
+ IgnoreError(mRouterTable.Release(router.GetRouterId()));
}
}
@@ -1807,7 +1782,6 @@
#endif
#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE
- // Retrieve registered multicast addresses of the Child
if (aChild.HasAnyMlrRegisteredAddress())
{
OT_ASSERT(aChild.IsStateValid());
@@ -1871,7 +1845,8 @@
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
if (mMaxChildIpAddresses > 0 && storedCount >= mMaxChildIpAddresses)
{
- // Skip remaining address registration entries but keep logging skipped addresses.
+ // Skip remaining address registration entries but keep logging
+ // skipped addresses.
error = kErrorNoBufs;
}
else
@@ -1917,7 +1892,7 @@
}
else
{
- // if not able to store DUA, then assume child does not have one
+ // It cannot store DUA, then assume child does not have one.
hasNewDua = false;
}
}
@@ -1999,43 +1974,33 @@
VerifyOrExit(IsRouterEligible(), error = kErrorInvalidState);
- // only process message when operating as a child, router, or leader
VerifyOrExit(IsAttached(), error = kErrorInvalidState);
- // Find Child
aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr);
child = mChildTable.FindChild(extAddr, Child::kInStateAnyExceptInvalid);
VerifyOrExit(child != nullptr, error = kErrorAlready);
- // Version
SuccessOrExit(error = Tlv::Find<VersionTlv>(aRxInfo.mMessage, version));
VerifyOrExit(version >= kThreadVersion1p1, error = kErrorParse);
- // Response
SuccessOrExit(error = aRxInfo.mMessage.ReadResponseTlv(response));
VerifyOrExit(response == child->GetChallenge(), error = kErrorSecurity);
- // Remove existing MLE messages
Get<MeshForwarder>().RemoveMessages(*child, Message::kSubTypeMleGeneral);
Get<MeshForwarder>().RemoveMessages(*child, Message::kSubTypeMleChildIdRequest);
Get<MeshForwarder>().RemoveMessages(*child, Message::kSubTypeMleChildUpdateRequest);
Get<MeshForwarder>().RemoveMessages(*child, Message::kSubTypeMleDataResponse);
- // Link-Layer and MLE Frame Counters
SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter));
- // Mode
SuccessOrExit(error = Tlv::Find<ModeTlv>(aRxInfo.mMessage, modeBitmask));
mode.Set(modeBitmask);
- // Timeout
SuccessOrExit(error = Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout));
- // Requested TLVs
SuccessOrExit(error = aRxInfo.mMessage.ReadTlvRequestTlv(tlvList));
- // Supervision interval
switch (Tlv::Find<SupervisionIntervalTlv>(aRxInfo.mMessage, supervisionInterval))
{
case kErrorNone:
@@ -2048,7 +2013,6 @@
ExitNow(error = kErrorParse);
}
- // Active Timestamp
switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, timestamp))
{
case kErrorNone:
@@ -2067,7 +2031,6 @@
ExitNow(error = kErrorParse);
}
- // Pending Timestamp
switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, timestamp))
{
case kErrorNone:
@@ -2093,12 +2056,10 @@
SuccessOrExit(error = ProcessAddressRegistrationTlv(aRxInfo, *child));
}
- // Remove from router table
router = mRouterTable.FindRouter(extAddr);
if (router != nullptr)
{
- // The `router` here can be invalid
RemoveNeighbor(*router);
}
@@ -2118,7 +2079,7 @@
child->SetKeySequence(aRxInfo.mKeySequence);
child->SetDeviceMode(mode);
child->SetVersion(version);
- child->GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss());
+ child->GetLinkInfo().AddRss(aRxInfo.mMessage.GetAverageRss());
child->SetTimeout(timeout);
child->SetSupervisionInterval(supervisionInterval);
#if OPENTHREAD_CONFIG_MULTI_RADIO
@@ -2179,11 +2140,9 @@
Log(kMessageReceive, kTypeChildUpdateRequestOfChild, aRxInfo.mMessageInfo.GetPeerAddr());
- // Mode
SuccessOrExit(error = Tlv::Find<ModeTlv>(aRxInfo.mMessage, modeBitmask));
mode.Set(modeBitmask);
- // Challenge
switch (aRxInfo.mMessage.ReadChallengeTlv(challenge))
{
case kErrorNone:
@@ -2236,7 +2195,6 @@
tlvList.Add(Tlv::kLinkFrameCounter);
}
- // IPv6 Address TLV
switch (ProcessAddressRegistrationTlv(aRxInfo, *child))
{
case kErrorNone:
@@ -2248,7 +2206,6 @@
ExitNow(error = kErrorParse);
}
- // Leader Data
switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData))
{
case kErrorNone:
@@ -2260,7 +2217,6 @@
ExitNow(error = kErrorParse);
}
- // Timeout
switch (Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout))
{
case kErrorNone:
@@ -2280,7 +2236,6 @@
ExitNow(error = kErrorParse);
}
- // Supervision interval
switch (Tlv::Find<SupervisionIntervalTlv>(aRxInfo.mMessage, supervisionInterval))
{
case kErrorNone:
@@ -2298,7 +2253,6 @@
child->SetSupervisionInterval(supervisionInterval);
- // TLV Request
switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList))
{
case kErrorNone:
@@ -2408,7 +2362,6 @@
child = static_cast<Child *>(aRxInfo.mNeighbor);
- // Response
switch (aRxInfo.mMessage.ReadResponseTlv(response))
{
case kErrorNone:
@@ -2424,7 +2377,6 @@
Log(kMessageReceive, kTypeChildUpdateResponseOfChild, aRxInfo.mMessageInfo.GetPeerAddr(), child->GetRloc16());
- // Source Address
switch (Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress))
{
case kErrorNone:
@@ -2443,7 +2395,6 @@
ExitNow(error = kErrorParse);
}
- // Status
switch (Tlv::Find<StatusTlv>(aRxInfo.mMessage, status))
{
case kErrorNone:
@@ -2455,8 +2406,6 @@
ExitNow(error = kErrorParse);
}
- // Link-Layer Frame Counter
-
switch (Tlv::Find<LinkFrameCounterTlv>(aRxInfo.mMessage, linkFrameCounter))
{
case kErrorNone:
@@ -2469,7 +2418,6 @@
ExitNow(error = kErrorParse);
}
- // MLE Frame Counter
switch (Tlv::Find<MleFrameCounterTlv>(aRxInfo.mMessage, mleFrameCounter))
{
case kErrorNone:
@@ -2481,7 +2429,6 @@
ExitNow(error = kErrorNone);
}
- // Timeout
switch (Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout))
{
case kErrorNone:
@@ -2508,7 +2455,6 @@
}
}
- // IPv6 Address
switch (ProcessAddressRegistrationTlv(aRxInfo, *child))
{
case kErrorNone:
@@ -2518,7 +2464,6 @@
ExitNow(error = kErrorParse);
}
- // Leader Data
switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData))
{
case kErrorNone:
@@ -2533,7 +2478,7 @@
SetChildStateToValid(*child);
child->SetLastHeard(TimerMilli::GetNow());
child->SetKeySequence(aRxInfo.mKeySequence);
- child->GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss());
+ child->GetLinkInfo().AddRss(aRxInfo.mMessage.GetAverageRss());
aRxInfo.mClass = response.IsEmpty() ? RxInfo::kPeerMessage : RxInfo::kAuthoritativeMessage;
@@ -2551,10 +2496,8 @@
VerifyOrExit(aRxInfo.IsNeighborStateValid(), error = kErrorSecurity);
- // TLV Request
SuccessOrExit(error = aRxInfo.mMessage.ReadTlvRequestTlv(tlvList));
- // Active Timestamp
switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, timestamp))
{
case kErrorNone:
@@ -2573,7 +2516,6 @@
ExitNow(error = kErrorParse);
}
- // Pending Timestamp
switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, timestamp))
{
case kErrorNone:
@@ -2685,7 +2627,6 @@
discoveryRequestTlv.SetLength(0);
- // only Routers and REEDs respond
VerifyOrExit(IsRouterEligible(), error = kErrorInvalidState);
SuccessOrExit(error = Tlv::FindTlvValueStartEndOffsets(aRxInfo.mMessage, Tlv::kDiscovery, offset, end));
@@ -2767,13 +2708,11 @@
message->SetRadioType(aDiscoverRequestMessage.GetRadioType());
#endif
- // Discovery TLV
tlv.SetType(Tlv::kDiscovery);
SuccessOrExit(error = message->Append(tlv));
startOffset = message->GetLength();
- // Discovery Response TLV
discoveryResponseTlv.Init();
discoveryResponseTlv.SetVersion(kThreadVersion);
@@ -2798,11 +2737,9 @@
SuccessOrExit(error = discoveryResponseTlv.AppendTo(*message));
- // Extended PAN ID TLV
SuccessOrExit(
error = Tlv::Append<MeshCoP::ExtendedPanIdTlv>(*message, Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId()));
- // Network Name TLV
networkNameTlv.Init();
networkNameTlv.SetNetworkName(Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsData());
SuccessOrExit(error = networkNameTlv.AppendTo(*message));
@@ -2843,7 +2780,7 @@
{
uint16_t rloc16;
- // pick next Child ID that is not being used
+ // Pick next Child ID that is not being used
do
{
mNextChildId++;
@@ -2857,7 +2794,6 @@
} while (mChildTable.FindChild(rloc16, Child::kInStateAnyExceptInvalid) != nullptr);
- // allocate Child ID
aChild.SetRloc16(rloc16);
}
@@ -2937,14 +2873,16 @@
{
if (msg.GetChildMask(childIndex) && msg.GetSubType() == Message::kSubTypeMleChildUpdateRequest)
{
- // No need to send the resync "Child Update Request" to the sleepy child
- // if there is one already queued.
+ // No need to send the resync "Child Update Request"
+ // to the sleepy child if there is one already
+ // queued.
if (aChild.IsStateRestoring())
{
ExitNow();
}
- // Remove queued outdated "Child Update Request" when there is newer Network Data is to send.
+ // Remove queued outdated "Child Update Request" when
+ // there is newer Network Data is to send.
Get<MeshForwarder>().RemoveMessages(aChild, Message::kSubTypeMleChildUpdateRequest);
break;
}
@@ -3155,10 +3093,8 @@
if (aDelay)
{
- // Remove MLE Data Responses from Send Message Queue.
Get<MeshForwarder>().RemoveDataResponseMessages();
- // Remove multicast MLE Data Response from Delayed Message Queue.
RemoveDelayedDataResponseMessage();
SuccessOrExit(error = message->SendAfterDelay(aDestination, aDelay));
@@ -3240,7 +3176,6 @@
if (aNeighbor.IsFullThreadDevice())
{
- // Clear all EID-to-RLOC entries associated with the child.
Get<AddressResolver>().RemoveEntriesForRloc16(aNeighbor.GetRloc16());
}
@@ -3291,11 +3226,9 @@
ExitNow();
}
- // loop exists
router = mRouterTable.FindRouterByRloc16(aDestRloc16);
VerifyOrExit(router != nullptr);
- // invalidate next hop
router->SetNextHopToInvalid();
ResetAdvertiseInterval();
@@ -3305,46 +3238,39 @@
Error MleRouter::CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header)
{
- Error error = kErrorNone;
+ bool isReachable = false;
if (IsChild())
{
- error = Mle::CheckReachability(aMeshDest, aIp6Header);
+ if (aMeshDest == GetRloc16())
+ {
+ isReachable = Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination());
+ }
+ else
+ {
+ isReachable = true;
+ }
+
ExitNow();
}
- if (aMeshDest == Get<Mac::Mac>().GetShortAddress())
+ if (aMeshDest == GetRloc16())
{
- // mesh destination is this device
- if (Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination()))
- {
- // IPv6 destination is this device
- ExitNow();
- }
- else if (mNeighborTable.FindNeighbor(aIp6Header.GetDestination()) != nullptr)
- {
- // IPv6 destination is an RFD child
- ExitNow();
- }
- }
- else if (RouterIdFromRloc16(aMeshDest) == mRouterId)
- {
- // mesh destination is a child of this device
- if (mChildTable.FindChild(aMeshDest, Child::kInStateValidOrRestoring))
- {
- ExitNow();
- }
- }
- else if (GetNextHop(aMeshDest) != Mac::kShortAddrInvalid)
- {
- // forwarding to another router and route is known
+ isReachable = Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination()) ||
+ (mNeighborTable.FindNeighbor(aIp6Header.GetDestination()) != nullptr);
ExitNow();
}
- error = kErrorNoRoute;
+ if (RouterIdFromRloc16(aMeshDest) == mRouterId)
+ {
+ isReachable = (mChildTable.FindChild(aMeshDest, Child::kInStateValidOrRestoring) != nullptr);
+ ExitNow();
+ }
+
+ isReachable = (GetNextHop(aMeshDest) != Mac::kShortAddrInvalid);
exit:
- return error;
+ return isReachable ? kErrorNone : kErrorNoRoute;
}
Error MleRouter::SendAddressSolicit(ThreadStatusTlv::Status aStatus)
@@ -3458,7 +3384,6 @@
SuccessOrExit(Tlv::FindTlv(*aMessage, routerMaskTlv));
VerifyOrExit(routerMaskTlv.IsValid());
- // assign short address
SetRouterId(routerId);
SetStateRouter(Rloc16FromRouterId(mRouterId));
@@ -3521,7 +3446,6 @@
}
exit:
- // Send announce after received address solicit reply if needed
InformPreviousChannel();
}
@@ -3582,7 +3506,6 @@
}
#endif
- // Check if allocation already exists
router = mRouterTable.FindRouter(extAddress);
if (router != nullptr)
@@ -3674,7 +3597,10 @@
// If assigning a new RLOC16 (e.g., on promotion of a child to
// router role) we clear any address cache entries associated
- // with the old RLOC16.
+ // with the old RLOC16 unless the sender is a direct child. For
+ // direct children, we retain the cache entries to allow
+ // association with the promoted router's new RLOC16 upon
+ // receiving its Link Advertisement.
if ((aResponseStatus == ThreadStatusTlv::kSuccess) && (aRouter != nullptr))
{
@@ -3684,6 +3610,7 @@
oldRloc16 = aMessageInfo.GetPeerAddr().GetIid().GetLocator();
VerifyOrExit(oldRloc16 != aRouter->GetRloc16());
+ VerifyOrExit(!RouterIdMatch(oldRloc16, GetRloc16()));
Get<AddressResolver>().RemoveEntriesForRloc16(oldRloc16);
}
@@ -3760,13 +3687,11 @@
{
if (router.GetRloc16() == GetRloc16())
{
- // skip self
continue;
}
if (!router.IsStateValid())
{
- // skip non-neighbor routers
continue;
}
@@ -3966,20 +3891,6 @@
}
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-void MleRouter::HandleTimeSync(RxInfo &aRxInfo)
-{
- Log(kMessageReceive, kTypeTimeSync, aRxInfo.mMessageInfo.GetPeerAddr());
-
- VerifyOrExit(aRxInfo.IsNeighborStateValid());
-
- aRxInfo.mClass = RxInfo::kPeerMessage;
-
- Get<TimeSync>().HandleTimeSyncMessage(aRxInfo.mMessage);
-
-exit:
- return;
-}
-
Error MleRouter::SendTimeSync(void)
{
Error error = kErrorNone;
diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp
index 65bfe11..955674c 100644
--- a/src/core/thread/mle_router.hpp
+++ b/src/core/thread/mle_router.hpp
@@ -649,9 +649,6 @@
void HandleDataRequest(RxInfo &aRxInfo);
void HandleNetworkDataUpdateRouter(void);
void HandleDiscoveryRequest(RxInfo &aRxInfo);
-#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
- void HandleTimeSync(RxInfo &aRxInfo);
-#endif
Error ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo);
Error ReadAndProcessRouteTlvOnFed(RxInfo &aRxInfo, uint8_t aParentId);
@@ -666,10 +663,10 @@
const Ip6::MessageInfo &aMessageInfo);
void SendAddressRelease(void);
void SendAdvertisement(void);
- Error SendLinkAccept(const Ip6::MessageInfo &aMessageInfo,
- Neighbor *aNeighbor,
- const TlvList &aRequestedTlvList,
- const RxChallenge &aChallenge);
+ Error SendLinkAccept(const RxInfo &aRxInfo,
+ Neighbor *aNeighbor,
+ const TlvList &aRequestedTlvList,
+ const RxChallenge &aChallenge);
void SendParentResponse(Child *aChild, const RxChallenge &aChallenge, bool aRoutersOnlyRequest);
Error SendChildIdResponse(Child &aChild);
Error SendChildUpdateRequest(Child &aChild);
diff --git a/src/core/thread/network_data.cpp b/src/core/thread/network_data.cpp
index 30ebe00..28675ad 100644
--- a/src/core/thread/network_data.cpp
+++ b/src/core/thread/network_data.cpp
@@ -50,6 +50,9 @@
RegisterLogModule("NetworkData");
+//---------------------------------------------------------------------------------------------------------------------
+// NetworkData
+
Error NetworkData::CopyNetworkData(Type aType, uint8_t *aData, uint8_t &aDataLength) const
{
Error error;
@@ -402,153 +405,6 @@
return contains;
}
-void MutableNetworkData::RemoveTemporaryData(void)
-{
- NetworkDataTlv *cur = GetTlvsStart();
-
- while (cur < GetTlvsEnd())
- {
- switch (cur->GetType())
- {
- case NetworkDataTlv::kTypePrefix:
- {
- PrefixTlv *prefix = As<PrefixTlv>(cur);
-
- RemoveTemporaryDataIn(*prefix);
-
- if (prefix->GetSubTlvsLength() == 0)
- {
- RemoveTlv(cur);
- continue;
- }
-
- break;
- }
-
- case NetworkDataTlv::kTypeService:
- {
- ServiceTlv *service = As<ServiceTlv>(cur);
-
- RemoveTemporaryDataIn(*service);
-
- if (service->GetSubTlvsLength() == 0)
- {
- RemoveTlv(cur);
- continue;
- }
-
- break;
- }
-
- default:
- // remove temporary tlv
- if (!cur->IsStable())
- {
- RemoveTlv(cur);
- continue;
- }
-
- break;
- }
-
- cur = cur->GetNext();
- }
-}
-
-void MutableNetworkData::RemoveTemporaryDataIn(PrefixTlv &aPrefix)
-{
- NetworkDataTlv *cur = aPrefix.GetSubTlvs();
-
- while (cur < aPrefix.GetNext())
- {
- if (cur->IsStable())
- {
- switch (cur->GetType())
- {
- case NetworkDataTlv::kTypeBorderRouter:
- {
- BorderRouterTlv *borderRouter = As<BorderRouterTlv>(cur);
- ContextTlv *context = aPrefix.FindSubTlv<ContextTlv>();
-
- // Replace p_border_router_16
- for (BorderRouterEntry *entry = borderRouter->GetFirstEntry(); entry <= borderRouter->GetLastEntry();
- entry = entry->GetNext())
- {
- if ((entry->IsDhcp() || entry->IsConfigure()) && (context != nullptr))
- {
- entry->SetRloc(0xfc00 | context->GetContextId());
- }
- else
- {
- entry->SetRloc(0xfffe);
- }
- }
-
- break;
- }
-
- case NetworkDataTlv::kTypeHasRoute:
- {
- HasRouteTlv *hasRoute = As<HasRouteTlv>(cur);
-
- // Replace r_border_router_16
- for (HasRouteEntry *entry = hasRoute->GetFirstEntry(); entry <= hasRoute->GetLastEntry();
- entry = entry->GetNext())
- {
- entry->SetRloc(0xfffe);
- }
-
- break;
- }
-
- default:
- break;
- }
-
- // keep stable tlv
- cur = cur->GetNext();
- }
- else
- {
- // remove temporary tlv
- uint8_t subTlvSize = cur->GetSize();
- RemoveTlv(cur);
- aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - subTlvSize);
- }
- }
-}
-
-void MutableNetworkData::RemoveTemporaryDataIn(ServiceTlv &aService)
-{
- NetworkDataTlv *cur = aService.GetSubTlvs();
-
- while (cur < aService.GetNext())
- {
- if (cur->IsStable())
- {
- switch (cur->GetType())
- {
- case NetworkDataTlv::kTypeServer:
- As<ServerTlv>(cur)->SetServer16(Mle::ServiceAlocFromId(aService.GetServiceId()));
- break;
-
- default:
- break;
- }
-
- // keep stable tlv
- cur = cur->GetNext();
- }
- else
- {
- // remove temporary tlv
- uint8_t subTlvSize = cur->GetSize();
- RemoveTlv(cur);
- aService.SetSubTlvsLength(aService.GetSubTlvsLength() - subTlvSize);
- }
- }
-}
-
const PrefixTlv *NetworkData::FindPrefix(const uint8_t *aPrefix, uint8_t aPrefixLength) const
{
TlvIterator tlvIterator(mTlvs, mLength);
@@ -639,6 +495,251 @@
return match;
}
+void NetworkData::FindRlocs(BorderRouterFilter aBrFilter, RoleFilter aRoleFilter, Rlocs &aRlocs) const
+{
+ Iterator iterator = kIteratorInit;
+ OnMeshPrefixConfig prefix;
+ ExternalRouteConfig route;
+ ServiceConfig service;
+ Config config;
+
+ aRlocs.Clear();
+
+ while (true)
+ {
+ config.mOnMeshPrefix = &prefix;
+ config.mExternalRoute = &route;
+ config.mService = &service;
+ config.mLowpanContext = nullptr;
+
+ SuccessOrExit(Iterate(iterator, Mac::kShortAddrBroadcast, config));
+
+ if (config.mOnMeshPrefix != nullptr)
+ {
+ bool matches = true;
+
+ switch (aBrFilter)
+ {
+ case kAnyBrOrServer:
+ break;
+ case kBrProvidingExternalIpConn:
+ matches = prefix.mOnMesh && (prefix.mDefaultRoute || prefix.mDp);
+ break;
+ }
+
+ if (matches)
+ {
+ AddRloc16ToRlocs(prefix.mRloc16, aRlocs, aRoleFilter);
+ }
+ }
+ else if (config.mExternalRoute != nullptr)
+ {
+ AddRloc16ToRlocs(route.mRloc16, aRlocs, aRoleFilter);
+ }
+ else if (config.mService != nullptr)
+ {
+ switch (aBrFilter)
+ {
+ case kAnyBrOrServer:
+ AddRloc16ToRlocs(service.mServerConfig.mRloc16, aRlocs, aRoleFilter);
+ break;
+ case kBrProvidingExternalIpConn:
+ break;
+ }
+ }
+ }
+
+exit:
+ return;
+}
+
+uint8_t NetworkData::CountBorderRouters(RoleFilter aRoleFilter) const
+{
+ Rlocs rlocs;
+
+ FindRlocs(kBrProvidingExternalIpConn, aRoleFilter, rlocs);
+
+ return rlocs.GetLength();
+}
+
+bool NetworkData::ContainsBorderRouterWithRloc(uint16_t aRloc16) const
+{
+ Rlocs rlocs;
+
+ FindRlocs(kBrProvidingExternalIpConn, kAnyRole, rlocs);
+
+ return rlocs.Contains(aRloc16);
+}
+
+void NetworkData::AddRloc16ToRlocs(uint16_t aRloc16, Rlocs &aRlocs, RoleFilter aRoleFilter)
+{
+ switch (aRoleFilter)
+ {
+ case kAnyRole:
+ break;
+
+ case kRouterRoleOnly:
+ VerifyOrExit(Mle::IsActiveRouter(aRloc16));
+ break;
+
+ case kChildRoleOnly:
+ VerifyOrExit(!Mle::IsActiveRouter(aRloc16));
+ break;
+ }
+
+ VerifyOrExit(!aRlocs.Contains(aRloc16));
+ IgnoreError(aRlocs.PushBack(aRloc16));
+
+exit:
+ return;
+}
+
+Error NetworkData::FindDomainIdFor(const Ip6::Prefix &aPrefix, uint8_t &aDomainId) const
+{
+ Error error = kErrorNone;
+ const PrefixTlv *prefixTlv = FindPrefix(aPrefix);
+
+ VerifyOrExit(prefixTlv != nullptr, error = kErrorNotFound);
+ aDomainId = prefixTlv->GetDomainId();
+
+exit:
+ return error;
+}
+
+//---------------------------------------------------------------------------------------------------------------------
+// MutableNetworkData
+
+void MutableNetworkData::RemoveTemporaryData(void)
+{
+ NetworkDataTlv *cur = GetTlvsStart();
+
+ while (cur < GetTlvsEnd())
+ {
+ bool shouldRemove = false;
+
+ switch (cur->GetType())
+ {
+ case NetworkDataTlv::kTypePrefix:
+ shouldRemove = RemoveTemporaryDataIn(*As<PrefixTlv>(cur));
+ break;
+
+ case NetworkDataTlv::kTypeService:
+ shouldRemove = RemoveTemporaryDataIn(*As<ServiceTlv>(cur));
+ break;
+
+ default:
+ shouldRemove = !cur->IsStable();
+ break;
+ }
+
+ if (shouldRemove)
+ {
+ RemoveTlv(cur);
+ continue;
+ }
+
+ cur = cur->GetNext();
+ }
+}
+
+bool MutableNetworkData::RemoveTemporaryDataIn(PrefixTlv &aPrefix)
+{
+ NetworkDataTlv *cur = aPrefix.GetSubTlvs();
+
+ while (cur < aPrefix.GetNext())
+ {
+ if (cur->IsStable())
+ {
+ switch (cur->GetType())
+ {
+ case NetworkDataTlv::kTypeBorderRouter:
+ {
+ BorderRouterTlv *borderRouter = As<BorderRouterTlv>(cur);
+ ContextTlv *context = aPrefix.FindSubTlv<ContextTlv>();
+
+ // Replace p_border_router_16
+ for (BorderRouterEntry *entry = borderRouter->GetFirstEntry(); entry <= borderRouter->GetLastEntry();
+ entry = entry->GetNext())
+ {
+ if ((entry->IsDhcp() || entry->IsConfigure()) && (context != nullptr))
+ {
+ entry->SetRloc(0xfc00 | context->GetContextId());
+ }
+ else
+ {
+ entry->SetRloc(0xfffe);
+ }
+ }
+
+ break;
+ }
+
+ case NetworkDataTlv::kTypeHasRoute:
+ {
+ HasRouteTlv *hasRoute = As<HasRouteTlv>(cur);
+
+ // Replace r_border_router_16
+ for (HasRouteEntry *entry = hasRoute->GetFirstEntry(); entry <= hasRoute->GetLastEntry();
+ entry = entry->GetNext())
+ {
+ entry->SetRloc(0xfffe);
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ // keep stable tlv
+ cur = cur->GetNext();
+ }
+ else
+ {
+ // remove temporary tlv
+ uint8_t subTlvSize = cur->GetSize();
+ RemoveTlv(cur);
+ aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - subTlvSize);
+ }
+ }
+
+ return (aPrefix.GetSubTlvsLength() == 0);
+}
+
+bool MutableNetworkData::RemoveTemporaryDataIn(ServiceTlv &aService)
+{
+ NetworkDataTlv *cur = aService.GetSubTlvs();
+
+ while (cur < aService.GetNext())
+ {
+ if (cur->IsStable())
+ {
+ switch (cur->GetType())
+ {
+ case NetworkDataTlv::kTypeServer:
+ As<ServerTlv>(cur)->SetServer16(Mle::ServiceAlocFromId(aService.GetServiceId()));
+ break;
+
+ default:
+ break;
+ }
+
+ // keep stable tlv
+ cur = cur->GetNext();
+ }
+ else
+ {
+ // remove temporary tlv
+ uint8_t subTlvSize = cur->GetSize();
+ RemoveTlv(cur);
+ aService.SetSubTlvsLength(aService.GetSubTlvsLength() - subTlvSize);
+ }
+ }
+
+ return (aService.GetSubTlvsLength() == 0);
+}
+
NetworkDataTlv *MutableNetworkData::AppendTlv(uint16_t aTlvSize)
{
NetworkDataTlv *tlv;
@@ -675,190 +776,5 @@
void MutableNetworkData::RemoveTlv(NetworkDataTlv *aTlv) { Remove(aTlv, aTlv->GetSize()); }
-Error NetworkData::GetNextServer(Iterator &aIterator, uint16_t &aRloc16) const
-{
- Error error;
- OnMeshPrefixConfig prefixConfig;
- ExternalRouteConfig routeConfig;
- ServiceConfig serviceConfig;
- Config config;
-
- config.mOnMeshPrefix = &prefixConfig;
- config.mExternalRoute = &routeConfig;
- config.mService = &serviceConfig;
- config.mLowpanContext = nullptr;
-
- SuccessOrExit(error = Iterate(aIterator, Mac::kShortAddrBroadcast, config));
-
- if (config.mOnMeshPrefix != nullptr)
- {
- aRloc16 = config.mOnMeshPrefix->mRloc16;
- }
- else if (config.mExternalRoute != nullptr)
- {
- aRloc16 = config.mExternalRoute->mRloc16;
- }
- else if (config.mService != nullptr)
- {
- aRloc16 = config.mService->mServerConfig.mRloc16;
- }
- else
- {
- OT_ASSERT(false);
- }
-
-exit:
- return error;
-}
-
-Error NetworkData::FindBorderRouters(RoleFilter aRoleFilter, uint16_t aRlocs[], uint8_t &aRlocsLength) const
-{
- class Rlocs // Wrapper over an array of RLOC16s.
- {
- public:
- Rlocs(RoleFilter aRoleFilter, uint16_t *aRlocs, uint8_t aRlocsMaxLength)
- : mRoleFilter(aRoleFilter)
- , mRlocs(aRlocs)
- , mLength(0)
- , mMaxLength(aRlocsMaxLength)
- {
- }
-
- uint8_t GetLength(void) const { return mLength; }
-
- Error AddRloc16(uint16_t aRloc16)
- {
- // Add `aRloc16` into the array if it matches `RoleFilter` and
- // it is not in the array already. If we need to add the `aRloc16`
- // but there is no more room in the array, return `kErrorNoBufs`.
-
- Error error = kErrorNone;
- uint8_t index;
-
- switch (mRoleFilter)
- {
- case kAnyRole:
- break;
-
- case kRouterRoleOnly:
- VerifyOrExit(Mle::IsActiveRouter(aRloc16));
- break;
-
- case kChildRoleOnly:
- VerifyOrExit(!Mle::IsActiveRouter(aRloc16));
- break;
- }
-
- for (index = 0; index < mLength; index++)
- {
- if (mRlocs[index] == aRloc16)
- {
- break;
- }
- }
-
- if (index == mLength)
- {
- VerifyOrExit(mLength < mMaxLength, error = kErrorNoBufs);
- mRlocs[mLength++] = aRloc16;
- }
-
- exit:
- return error;
- }
-
- private:
- RoleFilter mRoleFilter;
- uint16_t *mRlocs;
- uint8_t mLength;
- uint8_t mMaxLength;
- };
-
- Error error = kErrorNone;
- Rlocs rlocs(aRoleFilter, aRlocs, aRlocsLength);
- Iterator iterator = kIteratorInit;
- ExternalRouteConfig route;
- OnMeshPrefixConfig prefix;
-
- while (GetNextExternalRoute(iterator, route) == kErrorNone)
- {
- SuccessOrExit(error = rlocs.AddRloc16(route.mRloc16));
- }
-
- iterator = kIteratorInit;
-
- while (GetNextOnMeshPrefix(iterator, prefix) == kErrorNone)
- {
- if (!prefix.mDefaultRoute || !prefix.mOnMesh)
- {
- continue;
- }
-
- SuccessOrExit(error = rlocs.AddRloc16(prefix.mRloc16));
- }
-
-exit:
- aRlocsLength = rlocs.GetLength();
- return error;
-}
-
-uint8_t NetworkData::CountBorderRouters(RoleFilter aRoleFilter) const
-{
- // We use an over-estimate of max number of border routers in the
- // Network Data using the facts that network data is limited to 254
- // bytes and that an external route entry uses at minimum 3 bytes
- // for RLOC16 and flag, so `ceil(254/3) = 85`.
-
- static constexpr uint16_t kMaxRlocs = 85;
-
- uint16_t rlocs[kMaxRlocs];
- uint8_t rlocsLength = kMaxRlocs;
-
- SuccessOrAssert(FindBorderRouters(aRoleFilter, rlocs, rlocsLength));
-
- return rlocsLength;
-}
-
-bool NetworkData::ContainsBorderRouterWithRloc(uint16_t aRloc16) const
-{
- bool contains = false;
- Iterator iterator = kIteratorInit;
- ExternalRouteConfig route;
- OnMeshPrefixConfig prefix;
-
- while (GetNextExternalRoute(iterator, route) == kErrorNone)
- {
- if (route.mRloc16 == aRloc16)
- {
- ExitNow(contains = true);
- }
- }
-
- iterator = kIteratorInit;
-
- while (GetNextOnMeshPrefix(iterator, prefix) == kErrorNone)
- {
- if ((prefix.mRloc16 == aRloc16) && prefix.mOnMesh && (prefix.mDefaultRoute || prefix.mDp))
- {
- ExitNow(contains = true);
- }
- }
-
-exit:
- return contains;
-}
-
-Error NetworkData::FindDomainIdFor(const Ip6::Prefix &aPrefix, uint8_t &aDomainId) const
-{
- Error error = kErrorNone;
- const PrefixTlv *prefixTlv = FindPrefix(aPrefix);
-
- VerifyOrExit(prefixTlv != nullptr, error = kErrorNotFound);
- aDomainId = prefixTlv->GetDomainId();
-
-exit:
- return error;
-}
-
} // namespace NetworkData
} // namespace ot
diff --git a/src/core/thread/network_data.hpp b/src/core/thread/network_data.hpp
index 17c5151..d1d8270 100644
--- a/src/core/thread/network_data.hpp
+++ b/src/core/thread/network_data.hpp
@@ -325,18 +325,6 @@
bool ContainsEntriesFrom(const NetworkData &aCompare, uint16_t aRloc16) const;
/**
- * Provides the next server RLOC16 in the Thread Network Data.
- *
- * @param[in,out] aIterator A reference to the Network Data iterator.
- * @param[out] aRloc16 The RLOC16 value.
- *
- * @retval kErrorNone Successfully found the next server.
- * @retval kErrorNotFound No subsequent server exists in the Thread Network Data.
- *
- */
- Error GetNextServer(Iterator &aIterator, uint16_t &aRloc16) const;
-
- /**
* Finds and returns Domain ID associated with a given prefix in the Thread Network data.
*
* @param[in] aPrefix The prefix to search for.
@@ -349,30 +337,30 @@
Error FindDomainIdFor(const Ip6::Prefix &aPrefix, uint8_t &aDomainId) const;
/**
- * Finds and returns the list of RLOCs of border routers providing external IP connectivity.
+ * Finds border routers and servers in the Network Data matching specified filters, returning their RLOC16s.
*
- * A border router is considered to provide external IP connectivity if it has added at least one external route
- * entry, or an on-mesh prefix with default-route and on-mesh flags set.
+ * @p aBrFilter can be used to filter the type of BRs. It can be set to `kAnyBrOrServer` to include all BRs and
+ * servers. `kBrProvidingExternalIpConn` restricts it to BRs providing external IP connectivity where at least one
+ * the below conditions hold:
+ *
+ * - It has added at least one external route entry.
+ * - It has added at least one prefix entry with default-route and on-mesh flags set.
+ * - It has added at least one domain prefix (domain and on-mesh flags set).
*
* Should be used when the RLOC16s are present in the Network Data (when the Network Data contains the
* full set and not the stable subset).
*
- * @param[in] aRoleFilter Indicates which devices to include (any role, router role only, or child only).
- * @param[out] aRlocs Array to output the list of RLOCs.
- * @param[in,out] aRlocsLength On entry, @p aRlocs array length (max number of elements).
- * On exit, number RLOC16 entries added in @p aRlocs.
- *
- * @retval kErrorNone Successfully found all RLOC16s and updated @p aRlocs and @p aRlocsLength.
- * @retval kErrorNoBufs Ran out of space in @p aRlocs array. @p aRlocs and @p aRlocsLength are still updated up
- * to the maximum array length.
+ * @param[in] aBrFilter Indicates BR filter.
+ * @param[in] aRoleFilter Indicates role filter (any role, router role only, or child only).
+ * @param[out] aRlocs Array to output the list of RLOC16s.
*
*/
- Error FindBorderRouters(RoleFilter aRoleFilter, uint16_t aRlocs[], uint8_t &aRlocsLength) const;
+ void FindRlocs(BorderRouterFilter aBrFilter, RoleFilter aRoleFilter, Rlocs &aRlocs) const;
/**
* Counts the number of border routers providing external IP connectivity.
*
- * A border router is considered to provide external IP connectivity if at least one of the below conditions hold
+ * A border router is considered to provide external IP connectivity if at least one of the below conditions hold:
*
* - It has added at least one external route entry.
* - It has added at least one prefix entry with default-route and on-mesh flags set.
@@ -591,6 +579,8 @@
const ServiceData &aServiceData,
ServiceMatchMode aServiceMatchMode);
+ static void AddRloc16ToRlocs(uint16_t aRloc16, Rlocs &aRlocs, RoleFilter aRoleFilter);
+
const uint8_t *mTlvs;
uint8_t mLength;
};
@@ -779,8 +769,8 @@
void RemoveTemporaryData(void);
private:
- void RemoveTemporaryDataIn(PrefixTlv &aPrefix);
- void RemoveTemporaryDataIn(ServiceTlv &aService);
+ bool RemoveTemporaryDataIn(PrefixTlv &aPrefix);
+ bool RemoveTemporaryDataIn(ServiceTlv &aService);
uint8_t mSize;
};
diff --git a/src/core/thread/network_data_leader_ftd.cpp b/src/core/thread/network_data_leader_ftd.cpp
index fe423ef..5cfc44e 100644
--- a/src/core/thread/network_data_leader_ftd.cpp
+++ b/src/core/thread/network_data_leader_ftd.cpp
@@ -680,10 +680,7 @@
if (!mIsClone)
#endif
{
- if (error != kErrorNone)
- {
- LogNote("Failed to register network data: %s", ErrorToString(error));
- }
+ LogWarnOnError(error, "register network data");
}
}
@@ -1218,10 +1215,10 @@
{
const PrefixTlv *prefix;
TlvIterator tlvIterator(GetTlvsStart(), GetTlvsEnd());
- Iterator iterator = kIteratorInit;
ChangedFlags flags;
uint16_t rloc16;
uint16_t sessionId;
+ Rlocs rlocs;
mWaitingForNetDataSync = false;
@@ -1232,16 +1229,13 @@
// got the chance to send the updated Network Data to other
// routers.
- while (GetNextServer(iterator, rloc16) == kErrorNone)
- {
- if (!Get<RouterTable>().IsAllocated(Mle::RouterIdFromRloc16(rloc16)))
- {
- // After we `RemoveRloc()` the Network Data gets changed
- // and the `iterator` will not be valid anymore. So we set
- // it to `kIteratorInit` to restart the loop.
+ FindRlocs(kAnyBrOrServer, kAnyRole, rlocs);
- RemoveRloc(rloc16, kMatchModeRouterId, flags);
- iterator = kIteratorInit;
+ for (uint16_t rloc : rlocs)
+ {
+ if (!Get<RouterTable>().IsAllocated(Mle::RouterIdFromRloc16(rloc)))
+ {
+ RemoveRloc(rloc, kMatchModeRouterId, flags);
}
}
diff --git a/src/core/thread/network_data_notifier.cpp b/src/core/thread/network_data_notifier.cpp
index 750b379..e451a73 100644
--- a/src/core/thread/network_data_notifier.cpp
+++ b/src/core/thread/network_data_notifier.cpp
@@ -129,13 +129,14 @@
// - `kErrorNoBufs` if could not allocate message to send message.
// - `kErrorNotFound` if no stale child entries were found.
- Error error = kErrorNotFound;
- Iterator iterator = kIteratorInit;
- uint16_t rloc16;
+ Error error = kErrorNotFound;
+ Rlocs rlocs;
VerifyOrExit(Get<Mle::MleRouter>().IsRouterOrLeader());
- while (Get<Leader>().GetNextServer(iterator, rloc16) == kErrorNone)
+ Get<Leader>().FindRlocs(kAnyBrOrServer, kAnyRole, rlocs);
+
+ for (uint16_t rloc16 : rlocs)
{
if (!Mle::IsActiveRouter(rloc16) && Mle::RouterIdMatch(Get<Mle::MleRouter>().GetRloc16(), rloc16) &&
Get<ChildTable>().FindChild(rloc16, Child::kInStateValid) == nullptr)
diff --git a/src/core/thread/network_data_publisher.cpp b/src/core/thread/network_data_publisher.cpp
index ec925b8..a64f0c3 100644
--- a/src/core/thread/network_data_publisher.cpp
+++ b/src/core/thread/network_data_publisher.cpp
@@ -661,16 +661,17 @@
case kTypeUnicastMeshLocalEid:
{
Service::DnsSrpAnycast::Info anycastInfo;
+ bool hasServiceDataEntry;
- CountUnicastEntries(numEntries, numPreferredEntries);
+ CountServerDataUnicastEntries(numEntries, numPreferredEntries, hasServiceDataEntry);
desiredNumEntries = kDesiredNumUnicast;
- if (Get<Service::Manager>().FindPreferredDnsSrpAnycastInfo(anycastInfo) == kErrorNone)
+ if (hasServiceDataEntry || (Get<Service::Manager>().FindPreferredDnsSrpAnycastInfo(anycastInfo) == kErrorNone))
{
- // If there is any anycast entry in netdata, we set the
- // desired number of unicast entries (with address added
- // in server TLV) to zero to remove any added unicast
- // entry.
+ // If there is any service data unicast entry or anycast
+ // entry, we set the desired number of server data
+ // unicast entries to zero to remove any such previously
+ // added unicast entry.
desiredNumEntries = 0;
}
@@ -680,7 +681,7 @@
case kTypeUnicast:
desiredNumEntries = kDesiredNumUnicast;
- CountUnicastEntries(numEntries, numPreferredEntries);
+ CountServiceDataUnicastEntries(numEntries, numPreferredEntries);
break;
}
@@ -721,9 +722,53 @@
}
}
-void Publisher::DnsSrpServiceEntry::CountUnicastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const
+void Publisher::DnsSrpServiceEntry::CountServerDataUnicastEntries(uint8_t &aNumEntries,
+ uint8_t &aNumPreferredEntries,
+ bool &aHasServiceDataEntry) const
{
- // Count the number of "DNS/SRP Unicast" service entries in
+ // Count the number of server data DNS/SRP unicast entries in the
+ // Network Data. Also determine whether there is any service data
+ // DNS/SRP unicast entry (update `aHasServiceDataEntry`).
+
+ const ServiceTlv *serviceTlv = nullptr;
+ ServiceData data;
+
+ aHasServiceDataEntry = false;
+
+ data.InitFrom(Service::DnsSrpUnicast::kServiceData);
+
+ while ((serviceTlv = Get<Leader>().FindNextThreadService(serviceTlv, data, NetworkData::kServicePrefixMatch)) !=
+ nullptr)
+ {
+ TlvIterator subTlvIterator(*serviceTlv);
+ const ServerTlv *serverSubTlv;
+
+ if (serviceTlv->GetServiceDataLength() >= sizeof(Service::DnsSrpUnicast::ServiceData))
+ {
+ aHasServiceDataEntry = true;
+ }
+
+ while (((serverSubTlv = subTlvIterator.Iterate<ServerTlv>())) != nullptr)
+ {
+ if (serverSubTlv->GetServerDataLength() < sizeof(Service::DnsSrpUnicast::ServerData))
+ {
+ continue;
+ }
+
+ aNumEntries++;
+
+ if (IsPreferred(serverSubTlv->GetServer16()))
+ {
+ aNumPreferredEntries++;
+ }
+ }
+ }
+}
+
+void Publisher::DnsSrpServiceEntry::CountServiceDataUnicastEntries(uint8_t &aNumEntries,
+ uint8_t &aNumPreferredEntries) const
+{
+ // Count the number of service data DNS/SRP unicast entries in
// the Network Data.
const ServiceTlv *serviceTlv = nullptr;
@@ -737,45 +782,18 @@
TlvIterator subTlvIterator(*serviceTlv);
const ServerTlv *serverSubTlv;
+ if (serviceTlv->GetServiceDataLength() < sizeof(Service::DnsSrpUnicast::ServiceData))
+ {
+ continue;
+ }
+
while (((serverSubTlv = subTlvIterator.Iterate<ServerTlv>())) != nullptr)
{
- if (serviceTlv->GetServiceDataLength() >= sizeof(Service::DnsSrpUnicast::ServiceData))
+ aNumEntries++;
+
+ if (IsPreferred(serverSubTlv->GetServer16()))
{
- aNumEntries++;
-
- // Generally, we prefer entries where the SRP/DNS server
- // address/port info is included in the service TLV data
- // over the ones where the info is included in the
- // server TLV data (i.e., we prefer infra-provided
- // SRP/DNS entry over a BR local one using ML-EID). If
- // our entry itself uses the service TLV data, then we
- // prefer based on the associated RLOC16.
-
- if (GetType() == kTypeUnicast)
- {
- if (IsPreferred(serverSubTlv->GetServer16()))
- {
- aNumPreferredEntries++;
- }
- }
- else
- {
- aNumPreferredEntries++;
- }
- }
-
- if (serverSubTlv->GetServerDataLength() >= sizeof(Service::DnsSrpUnicast::ServerData))
- {
- aNumEntries++;
-
- // If our entry also uses the server TLV data (with
- // ML-EID address), then the we prefer based on the
- // associated RLOC16.
-
- if ((GetType() == kTypeUnicastMeshLocalEid) && IsPreferred(serverSubTlv->GetServer16()))
- {
- aNumPreferredEntries++;
- }
+ aNumPreferredEntries++;
}
}
}
diff --git a/src/core/thread/network_data_publisher.hpp b/src/core/thread/network_data_publisher.hpp
index 3ac2c82..4503efb 100644
--- a/src/core/thread/network_data_publisher.hpp
+++ b/src/core/thread/network_data_publisher.hpp
@@ -437,7 +437,10 @@
void Notify(Event aEvent) const;
void Process(void);
void CountAnycastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const;
- void CountUnicastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const;
+ void CountServiceDataUnicastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const;
+ void CountServerDataUnicastEntries(uint8_t &aNumEntries,
+ uint8_t &aNumPreferredEntries,
+ bool &aHasServiceDataEntry) const;
Info mInfo;
Callback<DnsSrpServiceCallback> mCallback;
diff --git a/src/core/thread/network_data_types.hpp b/src/core/thread/network_data_types.hpp
index 1457aa2..af63ff5 100644
--- a/src/core/thread/network_data_types.hpp
+++ b/src/core/thread/network_data_types.hpp
@@ -38,6 +38,7 @@
#include <openthread/netdata.h>
+#include "common/array.hpp"
#include "common/as_core_type.hpp"
#include "common/clearable.hpp"
#include "common/data.hpp"
@@ -108,6 +109,38 @@
};
/**
+ * Represents the entry filter used when searching for RLOC16 of border routers or servers in the Network Data.
+ *
+ * Regarding `kBrProvidingExternalIpConn`, a border router is considered to provide external IP connectivity if at
+ * least one of the below conditions hold:
+ *
+ * - It has added at least one external route entry.
+ * - It has added at least one prefix entry with default-route and on-mesh flags set.
+ * - It has added at least one domain prefix (domain and on-mesh flags set).
+ *
+ */
+enum BorderRouterFilter : uint8_t
+{
+ kAnyBrOrServer, ///< Include any border router or server entry.
+ kBrProvidingExternalIpConn, ///< Include border routers providing external IP connectivity.
+};
+
+/**
+ * Maximum length of `Rlocs` array containing RLOC16 of all border routers and servers in the Network Data.
+ *
+ * This limit is derived from the maximum Network Data size (254 bytes) and the minimum size of an external route entry
+ * (3 bytes including the RLOC16 and flags) as `ceil(254/3) = 85`.
+ *
+ */
+static constexpr uint8_t kMaxRlocs = 85;
+
+/**
+ * An array containing RLOC16 of all border routers and server in the Network Data.
+ *
+ */
+typedef Array<uint16_t, kMaxRlocs> Rlocs;
+
+/**
* Indicates whether a given `int8_t` preference value is a valid route preference (i.e., one of the
* values from `RoutePreference` enumeration).
*
diff --git a/src/core/thread/panid_query_server.cpp b/src/core/thread/panid_query_server.cpp
index cfa1399..59dd591 100644
--- a/src/core/thread/panid_query_server.cpp
+++ b/src/core/thread/panid_query_server.cpp
@@ -124,7 +124,7 @@
exit:
FreeMessageOnError(message, error);
- MeshCoP::LogError("send panid conflict", error);
+ LogWarnOnError(error, "send panid conflict");
}
void PanIdQueryServer::HandleTimer(void)
diff --git a/src/core/thread/version.hpp b/src/core/thread/version.hpp
index c3584aa..0277ede 100644
--- a/src/core/thread/version.hpp
+++ b/src/core/thread/version.hpp
@@ -42,10 +42,12 @@
constexpr uint16_t kThreadVersion = OPENTHREAD_CONFIG_THREAD_VERSION; ///< Thread Version of this device.
-constexpr uint16_t kThreadVersion1p1 = OT_THREAD_VERSION_1_1; ///< Thread Version 1.1
-constexpr uint16_t kThreadVersion1p2 = OT_THREAD_VERSION_1_2; ///< Thread Version 1.2
-constexpr uint16_t kThreadVersion1p3 = OT_THREAD_VERSION_1_3; ///< Thread Version 1.3
+constexpr uint16_t kThreadVersion1p1 = OT_THREAD_VERSION_1_1; ///< Thread Version 1.1
+constexpr uint16_t kThreadVersion1p2 = OT_THREAD_VERSION_1_2; ///< Thread Version 1.2
+constexpr uint16_t kThreadVersion1p3 = OT_THREAD_VERSION_1_3; ///< Thread Version 1.3
+// Support projects on legacy "1.3.1" version, which is now "1.4"
constexpr uint16_t kThreadVersion1p3p1 = OT_THREAD_VERSION_1_3_1; ///< Thread Version 1.3.1
+constexpr uint16_t kThreadVersion1p4 = OT_THREAD_VERSION_1_4; ///< Thread Version 1.4
} // namespace ot
diff --git a/src/core/utils/channel_manager.cpp b/src/core/utils/channel_manager.cpp
index 7ed7df4..788a077 100644
--- a/src/core/utils/channel_manager.cpp
+++ b/src/core/utils/channel_manager.cpp
@@ -34,7 +34,9 @@
#include "channel_manager.hpp"
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+ (OPENTHREAD_FTD || \
+ (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
#include "common/code_utils.hpp"
#include "common/locator_getters.hpp"
@@ -54,26 +56,51 @@
: InstanceLocator(aInstance)
, mSupportedChannelMask(0)
, mFavoredChannelMask(0)
+#if OPENTHREAD_FTD
, mDelay(kMinimumDelay)
+#endif
, mChannel(0)
+ , mChannelSelected(0)
, mState(kStateIdle)
, mTimer(aInstance)
, mAutoSelectInterval(kDefaultAutoSelectInterval)
+#if OPENTHREAD_FTD
, mAutoSelectEnabled(false)
+#endif
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+ , mAutoSelectCslEnabled(false)
+#endif
, mCcaFailureRateThreshold(kCcaFailureRateThreshold)
{
}
void ChannelManager::RequestChannelChange(uint8_t aChannel)
{
- LogInfo("Request to change to channel %d with delay %d sec", aChannel, mDelay);
+#if OPENTHREAD_FTD
+ if (Get<Mle::Mle>().IsFullThreadDevice() && Get<Mle::Mle>().IsRxOnWhenIdle() && mAutoSelectEnabled)
+ {
+ RequestNetworkChannelChange(aChannel);
+ }
+#endif
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+ if (mAutoSelectCslEnabled)
+ {
+ ChangeCslChannel(aChannel);
+ }
+#endif
+}
+#if OPENTHREAD_FTD
+void ChannelManager::RequestNetworkChannelChange(uint8_t aChannel)
+{
+ // Check requested channel != current channel
if (aChannel == Get<Mac::Mac>().GetPanChannel())
{
LogInfo("Already operating on the requested channel %d", aChannel);
ExitNow();
}
+ LogInfo("Request to change to channel %d with delay %d sec", aChannel, mDelay);
if (mState == kStateChangeInProgress)
{
VerifyOrExit(mChannel != aChannel);
@@ -89,7 +116,36 @@
exit:
return;
}
+#endif
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+void ChannelManager::ChangeCslChannel(uint8_t aChannel)
+{
+ if (!(!Get<Mle::Mle>().IsRxOnWhenIdle() && Get<Mac::Mac>().IsCslEnabled()))
+ {
+ // cannot select or use other channel
+ ExitNow();
+ }
+
+ if (aChannel == Get<Mac::Mac>().GetCslChannel())
+ {
+ LogInfo("Already operating on the requested channel %d", aChannel);
+ ExitNow();
+ }
+
+ VerifyOrExit(Radio::IsCslChannelValid(aChannel));
+
+ LogInfo("Change to Csl channel %d now.", aChannel);
+
+ mChannel = aChannel;
+ Get<Mac::Mac>().SetCslChannel(aChannel);
+
+exit:
+ return;
+}
+#endif // (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+
+#if OPENTHREAD_FTD
Error ChannelManager::SetDelay(uint16_t aDelay)
{
Error error = kErrorNone;
@@ -153,6 +209,7 @@
mState = kStateIdle;
StartAutoSelectTimer();
}
+#endif // OPENTHREAD_FTD
void ChannelManager::HandleTimer(void)
{
@@ -160,12 +217,14 @@
{
case kStateIdle:
LogInfo("Auto-triggered channel select");
- IgnoreError(RequestChannelSelect(false));
+ IgnoreError(RequestAutoChannelSelect(false));
StartAutoSelectTimer();
break;
case kStateChangeRequested:
+#if OPENTHREAD_FTD
StartDatasetUpdate();
+#endif
break;
case kStateChangeInProgress:
@@ -236,6 +295,53 @@
return shouldAttempt;
}
+#if OPENTHREAD_FTD
+Error ChannelManager::RequestNetworkChannelSelect(bool aSkipQualityCheck)
+{
+ Error error = kErrorNone;
+
+ SuccessOrExit(error = RequestChannelSelect(aSkipQualityCheck));
+ RequestNetworkChannelChange(mChannelSelected);
+
+exit:
+ if ((error == kErrorAbort) || (error == kErrorAlready))
+ {
+ // ignore aborted channel change
+ error = kErrorNone;
+ }
+ return error;
+}
+#endif
+
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+Error ChannelManager::RequestCslChannelSelect(bool aSkipQualityCheck)
+{
+ Error error = kErrorNone;
+
+ SuccessOrExit(error = RequestChannelSelect(aSkipQualityCheck));
+ ChangeCslChannel(mChannelSelected);
+
+exit:
+ if ((error == kErrorAbort) || (error == kErrorAlready))
+ {
+ // ignore aborted channel change
+ error = kErrorNone;
+ }
+ return error;
+}
+#endif
+
+Error ChannelManager::RequestAutoChannelSelect(bool aSkipQualityCheck)
+{
+ Error error = kErrorNone;
+
+ SuccessOrExit(error = RequestChannelSelect(aSkipQualityCheck));
+ RequestChannelChange(mChannelSelected);
+
+exit:
+ return error;
+}
+
Error ChannelManager::RequestChannelSelect(bool aSkipQualityCheck)
{
Error error = kErrorNone;
@@ -246,17 +352,27 @@
VerifyOrExit(!Get<Mle::Mle>().IsDisabled(), error = kErrorInvalidState);
- VerifyOrExit(aSkipQualityCheck || ShouldAttemptChannelChange());
+ VerifyOrExit(aSkipQualityCheck || ShouldAttemptChannelChange(), error = kErrorAbort);
SuccessOrExit(error = FindBetterChannel(newChannel, newOccupancy));
- curChannel = Get<Mac::Mac>().GetPanChannel();
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+ if (Get<Mac::Mac>().IsCslEnabled() && (Get<Mac::Mac>().GetCslChannel() != 0))
+ {
+ curChannel = Get<Mac::Mac>().GetCslChannel();
+ }
+ else
+#endif
+ {
+ curChannel = Get<Mac::Mac>().GetPanChannel();
+ }
+
curOccupancy = Get<ChannelMonitor>().GetChannelOccupancy(curChannel);
if (newChannel == curChannel)
{
LogInfo("Already on best possible channel %d", curChannel);
- ExitNow();
+ ExitNow(error = kErrorAlready);
}
LogInfo("Cur channel %d, occupancy 0x%04x - Best channel %d, occupancy 0x%04x", curChannel, curOccupancy,
@@ -269,18 +385,13 @@
(static_cast<uint16_t>(curOccupancy - newOccupancy) < kThresholdToChangeChannel))
{
LogInfo("Occupancy rate diff too small to change channel");
- ExitNow();
+ ExitNow(error = kErrorAbort);
}
- RequestChannelChange(newChannel);
+ mChannelSelected = newChannel;
exit:
-
- if (error != kErrorNone)
- {
- LogInfo("Request to select better channel failed, error: %s", ErrorToString(error));
- }
-
+ LogWarnOnError(error, "select better channel");
return error;
}
#endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
@@ -289,7 +400,14 @@
{
VerifyOrExit(mState == kStateIdle);
+#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && \
+ OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+ if (mAutoSelectEnabled || mAutoSelectCslEnabled)
+#elif OPENTHREAD_FTD
if (mAutoSelectEnabled)
+#elif (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+ if (mAutoSelectCslEnabled)
+#endif
{
mTimer.Start(Time::SecToMsec(mAutoSelectInterval));
}
@@ -302,15 +420,29 @@
return;
}
-void ChannelManager::SetAutoChannelSelectionEnabled(bool aEnabled)
+#if OPENTHREAD_FTD
+void ChannelManager::SetAutoNetworkChannelSelectionEnabled(bool aEnabled)
{
if (aEnabled != mAutoSelectEnabled)
{
mAutoSelectEnabled = aEnabled;
- IgnoreError(RequestChannelSelect(false));
+ IgnoreError(RequestNetworkChannelSelect(false));
StartAutoSelectTimer();
}
}
+#endif
+
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+void ChannelManager::SetAutoCslChannelSelectionEnabled(bool aEnabled)
+{
+ if (aEnabled != mAutoSelectCslEnabled)
+ {
+ mAutoSelectCslEnabled = aEnabled;
+ IgnoreError(RequestAutoChannelSelect(false));
+ StartAutoSelectTimer();
+ }
+}
+#endif
Error ChannelManager::SetAutoChannelSelectionInterval(uint32_t aInterval)
{
@@ -321,9 +453,19 @@
mAutoSelectInterval = aInterval;
- if (mAutoSelectEnabled && (mState == kStateIdle) && mTimer.IsRunning() && (prevInterval != aInterval))
+#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && \
+ OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+ if (mAutoSelectEnabled || mAutoSelectCslEnabled)
+#elif OPENTHREAD_FTD
+ if (mAutoSelectEnabled)
+#elif (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+ if (mAutoSelectCslEnabled)
+#endif
{
- mTimer.StartAt(mTimer.GetFireTime() - Time::SecToMsec(prevInterval), Time::SecToMsec(aInterval));
+ if ((mState == kStateIdle) && mTimer.IsRunning() && (prevInterval != aInterval))
+ {
+ mTimer.StartAt(mTimer.GetFireTime() - Time::SecToMsec(prevInterval), Time::SecToMsec(aInterval));
+ }
}
exit:
diff --git a/src/core/utils/channel_manager.hpp b/src/core/utils/channel_manager.hpp
index ad46141..02d9305 100644
--- a/src/core/utils/channel_manager.hpp
+++ b/src/core/utils/channel_manager.hpp
@@ -36,7 +36,9 @@
#include "openthread-core-config.h"
-#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD
+#if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && \
+ (OPENTHREAD_FTD || \
+ (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE))
#include <openthread/platform/radio.h>
@@ -64,11 +66,13 @@
class ChannelManager : public InstanceLocator, private NonCopyable
{
public:
+#if OPENTHREAD_FTD
/**
* Minimum delay (in seconds) used for network channel change.
*
*/
static constexpr uint16_t kMinimumDelay = OPENTHREAD_CONFIG_CHANNEL_MANAGER_MINIMUM_DELAY;
+#endif
/**
* Initializes a `ChanelManager` object.
@@ -78,6 +82,7 @@
*/
explicit ChannelManager(Instance &aInstance);
+#if OPENTHREAD_FTD
/**
* Requests a Thread network channel change.
*
@@ -91,16 +96,18 @@
* @param[in] aChannel The new channel for the Thread network.
*
*/
- void RequestChannelChange(uint8_t aChannel);
+ void RequestNetworkChannelChange(uint8_t aChannel);
+#endif
/**
- * Gets the channel from the last successful call to `RequestChannelChange()`.
+ * Gets the channel from the last successful call to `RequestNetworkChannelChange()` or `ChangeCslChannel()`.
*
* @returns The last requested channel, or zero if there has been no channel change request yet.
*
*/
uint8_t GetRequestedChannel(void) const { return mChannel; }
+#if OPENTHREAD_FTD
/**
* Gets the delay (in seconds) used for a channel change.
*
@@ -122,9 +129,11 @@
*
*/
Error SetDelay(uint16_t aDelay);
+#endif // OPENTHREAD_FTD
+#if OPENTHREAD_FTD
/**
- * Requests that `ChannelManager` checks and selects a new channel and starts a channel change.
+ * Requests that `ChannelManager` checks and selects a new network channel and starts a network channel change.
*
* Unlike the `RequestChannelChange()` where the channel must be given as a parameter, this method asks the
* `ChannelManager` to select a channel by itself (based on the collected channel quality info).
@@ -142,7 +151,7 @@
* (@sa SetSupportedChannels, @sa SetFavoredChannels).
*
* 3) If the newly selected channel is different from the current channel, `ChannelManager` requests/starts the
- * channel change process (internally invoking a `RequestChannelChange()`).
+ * channel change process (internally invoking a `RequestNetworkChannelChange()`).
*
*
* @param[in] aSkipQualityCheck Indicates whether the quality check (step 1) should be skipped.
@@ -152,8 +161,40 @@
* @retval kErrorInvalidState Thread is not enabled or not enough data to select new channel.
*
*/
- Error RequestChannelSelect(bool aSkipQualityCheck);
+ Error RequestNetworkChannelSelect(bool aSkipQualityCheck);
+#endif // OPENTHREAD_FTD
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+ /**
+ * Requests that `ChannelManager` checks and selects a new Csl channel and starts a channel change.
+ *
+ * Once called, the `ChannelManager` will perform the following 3 steps:
+ *
+ * 1) `ChannelManager` decides if the channel change would be helpful. This check can be skipped if
+ * `aSkipQualityCheck` is set to true (forcing a channel selection to happen and skipping the quality check).
+ * This step uses the collected link quality metrics on the device (such as CCA failure rate, frame and message
+ * error rates per neighbor, etc.) to determine if the current channel quality is at the level that justifies
+ * a channel change.
+ *
+ * 2) If the first step passes, then `ChannelManager` selects a potentially better channel. It uses the collected
+ * channel occupancy data by `ChannelMonitor` module. The supported and favored channels are used at this step.
+ * (@sa SetSupportedChannels, @sa SetFavoredChannels).
+ *
+ * 3) If the newly selected channel is different from the current Csl channel, `ChannelManager` starts the
+ * channel change process (internally invoking a `ChangeCslChannel()`).
+ *
+ *
+ * @param[in] aSkipQualityCheck Indicates whether the quality check (step 1) should be skipped.
+ *
+ * @retval kErrorNone Channel selection finished successfully.
+ * @retval kErrorNotFound Supported channels is empty, therefore could not select a channel.
+ * @retval kErrorInvalidState Thread is not enabled or not enough data to select new channel.
+ *
+ */
+ Error RequestCslChannelSelect(bool aSkipQualityCheck);
+#endif // (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+
+#if OPENTHREAD_FTD
/**
* Enables/disables the auto-channel-selection functionality.
*
@@ -163,7 +204,7 @@
* @param[in] aEnabled Indicates whether to enable or disable this functionality.
*
*/
- void SetAutoChannelSelectionEnabled(bool aEnabled);
+ void SetAutoNetworkChannelSelectionEnabled(bool aEnabled);
/**
* Indicates whether the auto-channel-selection functionality is enabled or not.
@@ -171,7 +212,29 @@
* @returns TRUE if enabled, FALSE if disabled.
*
*/
- bool GetAutoChannelSelectionEnabled(void) const { return mAutoSelectEnabled; }
+ bool GetAutoNetworkChannelSelectionEnabled(void) const { return mAutoSelectEnabled; }
+#endif
+
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+ /**
+ * Enables/disables the auto-channel-selection functionality.
+ *
+ * When enabled, `ChannelManager` will periodically invoke a `RequestChannelSelect(false)`. The period interval
+ * can be set by `SetAutoChannelSelectionInterval()`.
+ *
+ * @param[in] aEnabled Indicates whether to enable or disable this functionality.
+ *
+ */
+ void SetAutoCslChannelSelectionEnabled(bool aEnabled);
+
+ /**
+ * Indicates whether the auto-channel-selection functionality is enabled or not.
+ *
+ * @returns TRUE if enabled, FALSE if disabled.
+ *
+ */
+ bool GetAutoCslChannelSelectionEnabled(void) const { return mAutoSelectCslEnabled; }
+#endif
/**
* Sets the period interval (in seconds) used by auto-channel-selection functionality.
@@ -244,7 +307,7 @@
// Retry interval to resend Pending Dataset in case of tx failure (in ms).
static constexpr uint32_t kPendingDatasetTxRetryInterval = 20000;
- // Maximum jitter/wait time to start a requested channel change (in ms).
+ // Maximum jitter/wait time to start a requested network channel change (in ms).
static constexpr uint32_t kRequestStartJitterInterval = 10000;
// The minimum number of RSSI samples required before using the collected data (by `ChannelMonitor`) to select
@@ -273,28 +336,45 @@
kStateChangeInProgress,
};
+#if OPENTHREAD_FTD
void StartDatasetUpdate(void);
static void HandleDatasetUpdateDone(Error aError, void *aContext);
void HandleDatasetUpdateDone(Error aError);
- void HandleTimer(void);
- void StartAutoSelectTimer(void);
+#endif
+ void HandleTimer(void);
+ void StartAutoSelectTimer(void);
+ Error RequestChannelSelect(bool aSkipQualityCheck);
+ Error RequestAutoChannelSelect(bool aSkipQualityCheck);
+ void RequestChannelChange(uint8_t aChannel);
#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE
Error FindBetterChannel(uint8_t &aNewChannel, uint16_t &aOccupancy);
bool ShouldAttemptChannelChange(void);
#endif
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+ void ChangeCslChannel(uint8_t aChannel);
+#endif
+
using ManagerTimer = TimerMilliIn<ChannelManager, &ChannelManager::HandleTimer>;
Mac::ChannelMask mSupportedChannelMask;
Mac::ChannelMask mFavoredChannelMask;
- uint16_t mDelay;
- uint8_t mChannel;
- State mState;
- ManagerTimer mTimer;
- uint32_t mAutoSelectInterval;
- bool mAutoSelectEnabled;
- uint16_t mCcaFailureRateThreshold;
+#if OPENTHREAD_FTD
+ uint16_t mDelay;
+#endif
+ uint8_t mChannel;
+ uint8_t mChannelSelected;
+ State mState;
+ ManagerTimer mTimer;
+ uint32_t mAutoSelectInterval;
+#if OPENTHREAD_FTD
+ bool mAutoSelectEnabled;
+#endif
+#if (OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_CHANNEL_MANAGER_CSL_CHANNEL_SELECT_ENABLE)
+ bool mAutoSelectCslEnabled;
+#endif
+ uint16_t mCcaFailureRateThreshold;
};
/**
diff --git a/src/core/utils/otns.cpp b/src/core/utils/otns.cpp
index f19185f..a130ca3 100644
--- a/src/core/utils/otns.cpp
+++ b/src/core/utils/otns.cpp
@@ -166,11 +166,9 @@
EmitStatus("coap=send,%d,%d,%d,%s,%s,%d", aMessage.GetMessageId(), aMessage.GetType(), aMessage.GetCode(), uriPath,
aMessageInfo.GetPeerAddr().ToString().AsCString(), aMessageInfo.GetPeerPort());
+
exit:
- if (error != kErrorNone)
- {
- LogWarn("EmitCoapSend failed: %s", ErrorToString(error));
- }
+ LogWarnOnError(error, "EmitCoapSend");
}
void Otns::EmitCoapReceive(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
@@ -183,10 +181,7 @@
EmitStatus("coap=recv,%d,%d,%d,%s,%s,%d", aMessage.GetMessageId(), aMessage.GetType(), aMessage.GetCode(), uriPath,
aMessageInfo.GetPeerAddr().ToString().AsCString(), aMessageInfo.GetPeerPort());
exit:
- if (error != kErrorNone)
- {
- LogWarn("EmitCoapReceive failed: %s", ErrorToString(error));
- }
+ LogWarnOnError(error, "EmitCoapReceive");
}
void Otns::EmitCoapSendFailure(Error aError, Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
@@ -200,10 +195,7 @@
uriPath, aMessageInfo.GetPeerAddr().ToString().AsCString(), aMessageInfo.GetPeerPort(),
ErrorToString(aError));
exit:
- if (error != kErrorNone)
- {
- LogWarn("EmitCoapSendFailure failed: %s", ErrorToString(error));
- }
+ LogWarnOnError(error, "EmitCoapSendFailure");
}
} // namespace Utils
diff --git a/src/lib/platform/reset_util.h b/src/lib/platform/reset_util.h
index 52f5681..56e75da 100644
--- a/src/lib/platform/reset_util.h
+++ b/src/lib/platform/reset_util.h
@@ -32,8 +32,8 @@
#if defined(OPENTHREAD_ENABLE_COVERAGE) && OPENTHREAD_ENABLE_COVERAGE && defined(__GNUC__)
#if __GNUC__ >= 11 || (defined(__clang__) && (defined(__APPLE__) && (__clang_major__ >= 13)) || \
(!defined(__APPLE__) && (__clang_major__ >= 12)))
-void __gcov_dump();
-void __gcov_reset();
+void __gcov_dump(void);
+void __gcov_reset(void);
static void flush_gcov(void)
{
diff --git a/src/lib/spinel/BUILD.gn b/src/lib/spinel/BUILD.gn
index f03d945..e3911c0 100644
--- a/src/lib/spinel/BUILD.gn
+++ b/src/lib/spinel/BUILD.gn
@@ -34,6 +34,8 @@
spinel_sources = [
"openthread-spinel-config.h",
+ "logger.hpp",
+ "logger.cpp",
"multi_frame_buffer.hpp",
"radio_spinel.cpp",
"radio_spinel.hpp",
diff --git a/src/lib/spinel/CMakeLists.txt b/src/lib/spinel/CMakeLists.txt
index b8be41c..1c21d6e 100644
--- a/src/lib/spinel/CMakeLists.txt
+++ b/src/lib/spinel/CMakeLists.txt
@@ -89,7 +89,11 @@
target_include_directories(openthread-spinel-ncp PUBLIC ${OT_PUBLIC_INCLUDES} PRIVATE ${COMMON_INCLUDES})
target_include_directories(openthread-spinel-rcp PUBLIC ${OT_PUBLIC_INCLUDES} PRIVATE ${COMMON_INCLUDES})
-target_sources(openthread-radio-spinel PRIVATE radio_spinel.cpp)
+target_sources(openthread-radio-spinel
+ PRIVATE
+ logger.cpp
+ radio_spinel.cpp
+)
target_sources(openthread-spinel-ncp PRIVATE ${COMMON_SOURCES})
target_sources(openthread-spinel-rcp PRIVATE ${COMMON_SOURCES})
diff --git a/src/lib/spinel/logger.cpp b/src/lib/spinel/logger.cpp
new file mode 100644
index 0000000..46636c7
--- /dev/null
+++ b/src/lib/spinel/logger.cpp
@@ -0,0 +1,748 @@
+/*
+ * 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.
+ */
+
+#include "logger.hpp"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <openthread/error.h>
+#include <openthread/logging.h>
+#include <openthread/platform/radio.h>
+
+#include "common/code_utils.hpp"
+#include "common/num_utils.hpp"
+#include "lib/spinel/spinel.h"
+
+namespace ot {
+namespace Spinel {
+
+Logger::Logger(const char *aModuleName)
+ : mModuleName(aModuleName)
+{
+}
+
+void Logger::LogIfFail(const char *aText, otError aError)
+{
+ OT_UNUSED_VARIABLE(aText);
+
+ if (aError != OT_ERROR_NONE && aError != OT_ERROR_NO_ACK)
+ {
+ LogWarn("%s: %s", aText, otThreadErrorToString(aError));
+ }
+}
+
+void Logger::LogCrit(const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_CRIT, mModuleName, aFormat, args);
+ va_end(args);
+}
+
+void Logger::LogWarn(const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_WARN, mModuleName, aFormat, args);
+ va_end(args);
+}
+
+void Logger::LogNote(const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_NOTE, mModuleName, aFormat, args);
+ va_end(args);
+}
+
+void Logger::LogInfo(const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_INFO, mModuleName, aFormat, args);
+ va_end(args);
+}
+
+void Logger::LogDebg(const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_DEBG, mModuleName, aFormat, args);
+ va_end(args);
+}
+
+uint32_t Logger::Snprintf(char *aDest, uint32_t aSize, const char *aFormat, ...)
+{
+ int len;
+ va_list args;
+
+ va_start(args, aFormat);
+ len = vsnprintf(aDest, static_cast<size_t>(aSize), aFormat, args);
+ va_end(args);
+
+ return (len < 0) ? 0 : Min(static_cast<uint32_t>(len), aSize - 1);
+}
+
+void Logger::LogSpinelFrame(const uint8_t *aFrame, uint16_t aLength, bool aTx)
+{
+ otError error = OT_ERROR_NONE;
+ char buf[OPENTHREAD_CONFIG_LOG_MAX_SIZE] = {0};
+ spinel_ssize_t unpacked;
+ uint8_t header;
+ uint32_t cmd;
+ spinel_prop_key_t key;
+ uint8_t *data;
+ spinel_size_t len;
+ const char *prefix = nullptr;
+ char *start = buf;
+ char *end = buf + sizeof(buf);
+
+ VerifyOrExit(otLoggingGetLevel() >= OT_LOG_LEVEL_DEBG);
+
+ prefix = aTx ? "Sent spinel frame" : "Received spinel frame";
+ unpacked = spinel_datatype_unpack(aFrame, aLength, "CiiD", &header, &cmd, &key, &data, &len);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+ start += Snprintf(start, static_cast<uint32_t>(end - start), "%s, flg:0x%x, iid:%d, tid:%u, cmd:%s", prefix,
+ SPINEL_HEADER_GET_FLAG(header), SPINEL_HEADER_GET_IID(header), SPINEL_HEADER_GET_TID(header),
+ spinel_command_to_cstr(cmd));
+ VerifyOrExit(cmd != SPINEL_CMD_RESET);
+
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", key:%s", spinel_prop_key_to_cstr(key));
+ VerifyOrExit(cmd != SPINEL_CMD_PROP_VALUE_GET);
+
+ switch (key)
+ {
+ case SPINEL_PROP_LAST_STATUS:
+ {
+ spinel_status_t status;
+
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &status);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", status:%s", spinel_status_to_cstr(status));
+ }
+ break;
+
+ case SPINEL_PROP_MAC_RAW_STREAM_ENABLED:
+ case SPINEL_PROP_MAC_SRC_MATCH_ENABLED:
+ case SPINEL_PROP_PHY_ENABLED:
+ case SPINEL_PROP_RADIO_COEX_ENABLE:
+ {
+ bool enabled;
+
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_BOOL_S, &enabled);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", enabled:%u", enabled);
+ }
+ break;
+
+ case SPINEL_PROP_PHY_CCA_THRESHOLD:
+ case SPINEL_PROP_PHY_FEM_LNA_GAIN:
+ case SPINEL_PROP_PHY_RX_SENSITIVITY:
+ case SPINEL_PROP_PHY_RSSI:
+ case SPINEL_PROP_PHY_TX_POWER:
+ {
+ const char *name = nullptr;
+ int8_t value;
+
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_INT8_S, &value);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+ switch (key)
+ {
+ case SPINEL_PROP_PHY_TX_POWER:
+ name = "power";
+ break;
+ case SPINEL_PROP_PHY_CCA_THRESHOLD:
+ name = "threshold";
+ break;
+ case SPINEL_PROP_PHY_FEM_LNA_GAIN:
+ name = "gain";
+ break;
+ case SPINEL_PROP_PHY_RX_SENSITIVITY:
+ name = "sensitivity";
+ break;
+ case SPINEL_PROP_PHY_RSSI:
+ name = "rssi";
+ break;
+ }
+
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%d", name, value);
+ }
+ break;
+
+ case SPINEL_PROP_MAC_PROMISCUOUS_MODE:
+ case SPINEL_PROP_MAC_SCAN_STATE:
+ case SPINEL_PROP_PHY_CHAN:
+ case SPINEL_PROP_RCP_CSL_ACCURACY:
+ case SPINEL_PROP_RCP_CSL_UNCERTAINTY:
+ {
+ const char *name = nullptr;
+ uint8_t value;
+
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &value);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+ switch (key)
+ {
+ case SPINEL_PROP_MAC_SCAN_STATE:
+ name = "state";
+ break;
+ case SPINEL_PROP_RCP_CSL_ACCURACY:
+ name = "accuracy";
+ break;
+ case SPINEL_PROP_RCP_CSL_UNCERTAINTY:
+ name = "uncertainty";
+ break;
+ case SPINEL_PROP_MAC_PROMISCUOUS_MODE:
+ name = "mode";
+ break;
+ case SPINEL_PROP_PHY_CHAN:
+ name = "channel";
+ break;
+ }
+
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
+ }
+ break;
+
+ case SPINEL_PROP_MAC_15_4_PANID:
+ case SPINEL_PROP_MAC_15_4_SADDR:
+ case SPINEL_PROP_MAC_SCAN_PERIOD:
+ case SPINEL_PROP_PHY_REGION_CODE:
+ {
+ const char *name = nullptr;
+ uint16_t value;
+
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &value);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+ switch (key)
+ {
+ case SPINEL_PROP_MAC_SCAN_PERIOD:
+ name = "period";
+ break;
+ case SPINEL_PROP_PHY_REGION_CODE:
+ name = "region";
+ break;
+ case SPINEL_PROP_MAC_15_4_SADDR:
+ name = "saddr";
+ break;
+ case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES:
+ name = "saddr";
+ break;
+ case SPINEL_PROP_MAC_15_4_PANID:
+ name = "panid";
+ break;
+ }
+
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:0x%04x", name, value);
+ }
+ break;
+
+ case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES:
+ {
+ uint16_t saddr;
+
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", saddr:");
+
+ if (len < sizeof(saddr))
+ {
+ start += Snprintf(start, static_cast<uint32_t>(end - start), "none");
+ }
+ else
+ {
+ while (len >= sizeof(saddr))
+ {
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &saddr);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ data += unpacked;
+ len -= static_cast<spinel_size_t>(unpacked);
+ start += Snprintf(start, static_cast<uint32_t>(end - start), "0x%04x ", saddr);
+ }
+ }
+ }
+ break;
+
+ case SPINEL_PROP_RCP_MAC_FRAME_COUNTER:
+ case SPINEL_PROP_RCP_TIMESTAMP:
+ {
+ const char *name;
+ uint32_t value;
+
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT32_S, &value);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+ name = (key == SPINEL_PROP_RCP_TIMESTAMP) ? "timestamp" : "counter";
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
+ }
+ break;
+
+ case SPINEL_PROP_RADIO_CAPS:
+ case SPINEL_PROP_RCP_API_VERSION:
+ case SPINEL_PROP_RCP_MIN_HOST_API_VERSION:
+ {
+ const char *name;
+ unsigned int value;
+
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &value);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+ switch (key)
+ {
+ case SPINEL_PROP_RADIO_CAPS:
+ name = "caps";
+ break;
+ case SPINEL_PROP_RCP_API_VERSION:
+ name = "version";
+ break;
+ case SPINEL_PROP_RCP_MIN_HOST_API_VERSION:
+ name = "min-host-version";
+ break;
+ default:
+ name = "";
+ break;
+ }
+
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
+ }
+ break;
+
+ case SPINEL_PROP_MAC_ENERGY_SCAN_RESULT:
+ case SPINEL_PROP_PHY_CHAN_MAX_POWER:
+ {
+ const char *name;
+ uint8_t channel;
+ int8_t value;
+
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S, &channel, &value);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+ name = (key == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT) ? "rssi" : "power";
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", channel:%u, %s:%d", channel, name, value);
+ }
+ break;
+
+ case SPINEL_PROP_CAPS:
+ {
+ unsigned int capability;
+
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", caps:");
+
+ while (len > 0)
+ {
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &capability);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ data += unpacked;
+ len -= static_cast<spinel_size_t>(unpacked);
+ start += Snprintf(start, static_cast<uint32_t>(end - start), "%s ", spinel_capability_to_cstr(capability));
+ }
+ }
+ break;
+
+ case SPINEL_PROP_PROTOCOL_VERSION:
+ {
+ unsigned int major;
+ unsigned int minor;
+
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S,
+ &major, &minor);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", major:%u, minor:%u", major, minor);
+ }
+ break;
+
+ case SPINEL_PROP_PHY_CHAN_PREFERRED:
+ case SPINEL_PROP_PHY_CHAN_SUPPORTED:
+ {
+ uint8_t maskBuffer[kChannelMaskBufferSize];
+ uint32_t channelMask = 0;
+ const uint8_t *maskData = maskBuffer;
+ spinel_size_t maskLength = sizeof(maskBuffer);
+
+ unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, maskBuffer, &maskLength);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+ while (maskLength > 0)
+ {
+ uint8_t channel;
+
+ unpacked = spinel_datatype_unpack(maskData, maskLength, SPINEL_DATATYPE_UINT8_S, &channel);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ VerifyOrExit(channel < kChannelMaskBufferSize, error = OT_ERROR_PARSE);
+ channelMask |= (1UL << channel);
+
+ maskData += unpacked;
+ maskLength -= static_cast<spinel_size_t>(unpacked);
+ }
+
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", channelMask:0x%08x", channelMask);
+ }
+ break;
+
+ case SPINEL_PROP_NCP_VERSION:
+ {
+ const char *version;
+
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &version);
+ VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE);
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", version:%s", version);
+ }
+ break;
+
+ case SPINEL_PROP_STREAM_RAW:
+ {
+ otRadioFrame frame;
+
+ if (cmd == SPINEL_CMD_PROP_VALUE_IS)
+ {
+ uint16_t flags;
+ int8_t noiseFloor;
+ unsigned int receiveError;
+
+ unpacked = spinel_datatype_unpack(data, len,
+ SPINEL_DATATYPE_DATA_WLEN_S // Frame
+ SPINEL_DATATYPE_INT8_S // RSSI
+ SPINEL_DATATYPE_INT8_S // Noise Floor
+ SPINEL_DATATYPE_UINT16_S // Flags
+ SPINEL_DATATYPE_STRUCT_S( // PHY-data
+ SPINEL_DATATYPE_UINT8_S // 802.15.4 channel
+ SPINEL_DATATYPE_UINT8_S // 802.15.4 LQI
+ SPINEL_DATATYPE_UINT64_S // Timestamp (us).
+ ) SPINEL_DATATYPE_STRUCT_S( // Vendor-data
+ SPINEL_DATATYPE_UINT_PACKED_S // Receive error
+ ),
+ &frame.mPsdu, &frame.mLength, &frame.mInfo.mRxInfo.mRssi, &noiseFloor,
+ &flags, &frame.mChannel, &frame.mInfo.mRxInfo.mLqi,
+ &frame.mInfo.mRxInfo.mTimestamp, &receiveError);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", len:%u, rssi:%d ...", frame.mLength,
+ frame.mInfo.mRxInfo.mRssi);
+ OT_UNUSED_VARIABLE(start); // Avoid static analysis error
+ LogDebg("%s", buf);
+
+ start = buf;
+ start += Snprintf(start, static_cast<uint32_t>(end - start),
+ "... noise:%d, flags:0x%04x, channel:%u, lqi:%u, timestamp:%lu, rxerr:%u", noiseFloor,
+ flags, frame.mChannel, frame.mInfo.mRxInfo.mLqi,
+ static_cast<unsigned long>(frame.mInfo.mRxInfo.mTimestamp), receiveError);
+ }
+ else if (cmd == SPINEL_CMD_PROP_VALUE_SET)
+ {
+ bool csmaCaEnabled;
+ bool isHeaderUpdated;
+ bool isARetx;
+ bool skipAes;
+
+ unpacked = spinel_datatype_unpack(
+ data, len,
+ SPINEL_DATATYPE_DATA_WLEN_S // Frame data
+ SPINEL_DATATYPE_UINT8_S // Channel
+ SPINEL_DATATYPE_UINT8_S // MaxCsmaBackoffs
+ SPINEL_DATATYPE_UINT8_S // MaxFrameRetries
+ SPINEL_DATATYPE_BOOL_S // CsmaCaEnabled
+ SPINEL_DATATYPE_BOOL_S // IsHeaderUpdated
+ SPINEL_DATATYPE_BOOL_S // IsARetx
+ SPINEL_DATATYPE_BOOL_S // SkipAes
+ SPINEL_DATATYPE_UINT32_S // TxDelay
+ SPINEL_DATATYPE_UINT32_S, // TxDelayBaseTime
+ &frame.mPsdu, &frame.mLength, &frame.mChannel, &frame.mInfo.mTxInfo.mMaxCsmaBackoffs,
+ &frame.mInfo.mTxInfo.mMaxFrameRetries, &csmaCaEnabled, &isHeaderUpdated, &isARetx, &skipAes,
+ &frame.mInfo.mTxInfo.mTxDelay, &frame.mInfo.mTxInfo.mTxDelayBaseTime);
+
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ start += Snprintf(start, static_cast<uint32_t>(end - start),
+ ", len:%u, channel:%u, maxbackoffs:%u, maxretries:%u ...", frame.mLength, frame.mChannel,
+ frame.mInfo.mTxInfo.mMaxCsmaBackoffs, frame.mInfo.mTxInfo.mMaxFrameRetries);
+ OT_UNUSED_VARIABLE(start); // Avoid static analysis error
+ LogDebg("%s", buf);
+
+ start = buf;
+ start += Snprintf(start, static_cast<uint32_t>(end - start),
+ "... csmaCaEnabled:%u, isHeaderUpdated:%u, isARetx:%u, skipAes:%u"
+ ", txDelay:%u, txDelayBase:%u",
+ csmaCaEnabled, isHeaderUpdated, isARetx, skipAes, frame.mInfo.mTxInfo.mTxDelay,
+ frame.mInfo.mTxInfo.mTxDelayBaseTime);
+ }
+ }
+ break;
+
+ case SPINEL_PROP_STREAM_DEBUG:
+ {
+ char debugString[OPENTHREAD_CONFIG_NCP_SPINEL_LOG_MAX_SIZE + 1];
+ spinel_size_t stringLength = sizeof(debugString);
+
+ unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, debugString, &stringLength);
+ assert(stringLength < sizeof(debugString));
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ debugString[stringLength] = '\0';
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", debug:%s", debugString);
+ }
+ break;
+
+ case SPINEL_PROP_STREAM_LOG:
+ {
+ const char *logString;
+ uint8_t logLevel;
+
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &logString);
+ VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE);
+ data += unpacked;
+ len -= static_cast<spinel_size_t>(unpacked);
+
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &logLevel);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", level:%u, log:%s", logLevel, logString);
+ }
+ break;
+
+ case SPINEL_PROP_NEST_STREAM_MFG:
+ {
+ const char *output;
+ size_t outputLen;
+
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &output, &outputLen);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", diag:%s", output);
+ }
+ break;
+
+ case SPINEL_PROP_RCP_MAC_KEY:
+ {
+ uint8_t keyIdMode;
+ uint8_t keyId;
+ otMacKey prevKey;
+ unsigned int prevKeyLen = sizeof(otMacKey);
+ otMacKey currKey;
+ unsigned int currKeyLen = sizeof(otMacKey);
+ otMacKey nextKey;
+ unsigned int nextKeyLen = sizeof(otMacKey);
+
+ unpacked = spinel_datatype_unpack(data, len,
+ SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_DATA_WLEN_S
+ SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_WLEN_S,
+ &keyIdMode, &keyId, prevKey.m8, &prevKeyLen, currKey.m8, &currKeyLen,
+ nextKey.m8, &nextKeyLen);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ start += Snprintf(start, static_cast<uint32_t>(end - start),
+ ", keyIdMode:%u, keyId:%u, prevKey:***, currKey:***, nextKey:***", keyIdMode, keyId);
+ }
+ break;
+
+ case SPINEL_PROP_HWADDR:
+ case SPINEL_PROP_MAC_15_4_LADDR:
+ {
+ const char *name = nullptr;
+ uint8_t m8[OT_EXT_ADDRESS_SIZE] = {0};
+
+ unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, &m8[0]);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+ name = (key == SPINEL_PROP_HWADDR) ? "eui64" : "laddr";
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%02x%02x%02x%02x%02x%02x%02x%02x", name,
+ m8[0], m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]);
+ }
+ break;
+
+ case SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES:
+ {
+ uint8_t m8[OT_EXT_ADDRESS_SIZE];
+
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", extaddr:");
+
+ if (len < sizeof(m8))
+ {
+ start += Snprintf(start, static_cast<uint32_t>(end - start), "none");
+ }
+ else
+ {
+ while (len >= sizeof(m8))
+ {
+ unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, m8);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ data += unpacked;
+ len -= static_cast<spinel_size_t>(unpacked);
+ start += Snprintf(start, static_cast<uint32_t>(end - start), "%02x%02x%02x%02x%02x%02x%02x%02x ", m8[0],
+ m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]);
+ }
+ }
+ }
+ break;
+
+ case SPINEL_PROP_RADIO_COEX_METRICS:
+ {
+ otRadioCoexMetrics metrics;
+ unpacked = spinel_datatype_unpack(
+ data, len,
+ SPINEL_DATATYPE_STRUCT_S( // Tx Coex Metrics Structure
+ SPINEL_DATATYPE_UINT32_S // NumTxRequest
+ SPINEL_DATATYPE_UINT32_S // NumTxGrantImmediate
+ SPINEL_DATATYPE_UINT32_S // NumTxGrantWait
+ SPINEL_DATATYPE_UINT32_S // NumTxGrantWaitActivated
+ SPINEL_DATATYPE_UINT32_S // NumTxGrantWaitTimeout
+ SPINEL_DATATYPE_UINT32_S // NumTxGrantDeactivatedDuringRequest
+ SPINEL_DATATYPE_UINT32_S // NumTxDelayedGrant
+ SPINEL_DATATYPE_UINT32_S // AvgTxRequestToGrantTime
+ ) SPINEL_DATATYPE_STRUCT_S( // Rx Coex Metrics Structure
+ SPINEL_DATATYPE_UINT32_S // NumRxRequest
+ SPINEL_DATATYPE_UINT32_S // NumRxGrantImmediate
+ SPINEL_DATATYPE_UINT32_S // NumRxGrantWait
+ SPINEL_DATATYPE_UINT32_S // NumRxGrantWaitActivated
+ SPINEL_DATATYPE_UINT32_S // NumRxGrantWaitTimeout
+ SPINEL_DATATYPE_UINT32_S // NumRxGrantDeactivatedDuringRequest
+ SPINEL_DATATYPE_UINT32_S // NumRxDelayedGrant
+ SPINEL_DATATYPE_UINT32_S // AvgRxRequestToGrantTime
+ SPINEL_DATATYPE_UINT32_S // NumRxGrantNone
+ ) SPINEL_DATATYPE_BOOL_S // Stopped
+ SPINEL_DATATYPE_UINT32_S, // NumGrantGlitch
+ &metrics.mNumTxRequest, &metrics.mNumTxGrantImmediate, &metrics.mNumTxGrantWait,
+ &metrics.mNumTxGrantWaitActivated, &metrics.mNumTxGrantWaitTimeout,
+ &metrics.mNumTxGrantDeactivatedDuringRequest, &metrics.mNumTxDelayedGrant,
+ &metrics.mAvgTxRequestToGrantTime, &metrics.mNumRxRequest, &metrics.mNumRxGrantImmediate,
+ &metrics.mNumRxGrantWait, &metrics.mNumRxGrantWaitActivated, &metrics.mNumRxGrantWaitTimeout,
+ &metrics.mNumRxGrantDeactivatedDuringRequest, &metrics.mNumRxDelayedGrant,
+ &metrics.mAvgRxRequestToGrantTime, &metrics.mNumRxGrantNone, &metrics.mStopped, &metrics.mNumGrantGlitch);
+
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+ LogDebg("%s ...", buf);
+ LogDebg(" txRequest:%lu", ToUlong(metrics.mNumTxRequest));
+ LogDebg(" txGrantImmediate:%lu", ToUlong(metrics.mNumTxGrantImmediate));
+ LogDebg(" txGrantWait:%lu", ToUlong(metrics.mNumTxGrantWait));
+ LogDebg(" txGrantWaitActivated:%lu", ToUlong(metrics.mNumTxGrantWaitActivated));
+ LogDebg(" txGrantWaitTimeout:%lu", ToUlong(metrics.mNumTxGrantWaitTimeout));
+ LogDebg(" txGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumTxGrantDeactivatedDuringRequest));
+ LogDebg(" txDelayedGrant:%lu", ToUlong(metrics.mNumTxDelayedGrant));
+ LogDebg(" avgTxRequestToGrantTime:%lu", ToUlong(metrics.mAvgTxRequestToGrantTime));
+ LogDebg(" rxRequest:%lu", ToUlong(metrics.mNumRxRequest));
+ LogDebg(" rxGrantImmediate:%lu", ToUlong(metrics.mNumRxGrantImmediate));
+ LogDebg(" rxGrantWait:%lu", ToUlong(metrics.mNumRxGrantWait));
+ LogDebg(" rxGrantWaitActivated:%lu", ToUlong(metrics.mNumRxGrantWaitActivated));
+ LogDebg(" rxGrantWaitTimeout:%lu", ToUlong(metrics.mNumRxGrantWaitTimeout));
+ LogDebg(" rxGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumRxGrantDeactivatedDuringRequest));
+ LogDebg(" rxDelayedGrant:%lu", ToUlong(metrics.mNumRxDelayedGrant));
+ LogDebg(" avgRxRequestToGrantTime:%lu", ToUlong(metrics.mAvgRxRequestToGrantTime));
+ LogDebg(" rxGrantNone:%lu", ToUlong(metrics.mNumRxGrantNone));
+ LogDebg(" stopped:%u", metrics.mStopped);
+
+ start = buf;
+ start += Snprintf(start, static_cast<uint32_t>(end - start), " grantGlitch:%u", metrics.mNumGrantGlitch);
+ }
+ break;
+
+ case SPINEL_PROP_MAC_SCAN_MASK:
+ {
+ constexpr uint8_t kNumChannels = 16;
+ uint8_t channels[kNumChannels];
+ spinel_size_t size;
+
+ unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_DATA_S, channels, &size);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", channels:");
+
+ for (spinel_size_t i = 0; i < size; i++)
+ {
+ start += Snprintf(start, static_cast<uint32_t>(end - start), "%u ", channels[i]);
+ }
+ }
+ break;
+
+ case SPINEL_PROP_RCP_ENH_ACK_PROBING:
+ {
+ uint16_t saddr;
+ uint8_t m8[OT_EXT_ADDRESS_SIZE];
+ uint8_t flags;
+
+ unpacked = spinel_datatype_unpack(
+ data, len, SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT8_S, &saddr, m8, &flags);
+
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ start += Snprintf(start, static_cast<uint32_t>(end - start),
+ ", saddr:%04x, extaddr:%02x%02x%02x%02x%02x%02x%02x%02x, flags:0x%02x", saddr, m8[0], m8[1],
+ m8[2], m8[3], m8[4], m8[5], m8[6], m8[7], flags);
+ }
+ break;
+
+ case SPINEL_PROP_PHY_CALIBRATED_POWER:
+ {
+ if (cmd == SPINEL_CMD_PROP_VALUE_INSERT)
+ {
+ uint8_t channel;
+ int16_t actualPower;
+ uint8_t *rawPowerSetting;
+ unsigned int rawPowerSettingLength;
+
+ unpacked = spinel_datatype_unpack(
+ data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_DATA_WLEN_S, &channel,
+ &actualPower, &rawPowerSetting, &rawPowerSettingLength);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+
+ start += Snprintf(start, static_cast<uint32_t>(end - start),
+ ", ch:%u, actualPower:%d, rawPowerSetting:", channel, actualPower);
+ for (unsigned int i = 0; i < rawPowerSettingLength; i++)
+ {
+ start += Snprintf(start, static_cast<uint32_t>(end - start), "%02x", rawPowerSetting[i]);
+ }
+ }
+ }
+ break;
+
+ case SPINEL_PROP_PHY_CHAN_TARGET_POWER:
+ {
+ uint8_t channel;
+ int16_t targetPower;
+
+ unpacked =
+ spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S, &channel, &targetPower);
+ VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
+ start += Snprintf(start, static_cast<uint32_t>(end - start), ", ch:%u, targetPower:%d", channel, targetPower);
+ }
+ break;
+ }
+
+exit:
+ OT_UNUSED_VARIABLE(start); // Avoid static analysis error
+ if (error == OT_ERROR_NONE)
+ {
+ LogDebg("%s", buf);
+ }
+ else if (prefix != nullptr)
+ {
+ LogDebg("%s, failed to parse spinel frame !", prefix);
+ }
+}
+
+} // namespace Spinel
+} // namespace ot
diff --git a/src/lib/spinel/logger.hpp b/src/lib/spinel/logger.hpp
new file mode 100644
index 0000000..ea86f4d
--- /dev/null
+++ b/src/lib/spinel/logger.hpp
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#ifndef SPINEL_LOGGER_HPP_
+#define SPINEL_LOGGER_HPP_
+
+#include "openthread-core-config.h"
+
+#include <openthread/error.h>
+#include <openthread/platform/toolchain.h>
+
+#include "ncp/ncp_config.h"
+
+namespace ot {
+namespace Spinel {
+
+class Logger
+{
+protected:
+ explicit Logger(const char *aModuleName);
+
+ void LogIfFail(const char *aText, otError aError);
+
+ void LogCrit(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
+ void LogWarn(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
+ void LogNote(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
+ void LogInfo(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
+ void LogDebg(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);
+
+ uint32_t Snprintf(char *aDest, uint32_t aSize, const char *aFormat, ...);
+ void LogSpinelFrame(const uint8_t *aFrame, uint16_t aLength, bool aTx);
+
+ enum
+ {
+ kChannelMaskBufferSize = 32, ///< Max buffer size used to store `SPINEL_PROP_PHY_CHAN_SUPPORTED` value.
+ };
+
+ const char *mModuleName;
+};
+
+} // namespace Spinel
+} // namespace ot
+
+#endif // SPINEL_LOG_HPP_
diff --git a/src/lib/spinel/multi_frame_buffer.hpp b/src/lib/spinel/multi_frame_buffer.hpp
index ab1ef83..61945a5 100644
--- a/src/lib/spinel/multi_frame_buffer.hpp
+++ b/src/lib/spinel/multi_frame_buffer.hpp
@@ -371,7 +371,7 @@
aFrame = (aFrame == nullptr) ? mBuffer : aFrame + aLength;
- if (aFrame != mWriteFrameStart)
+ if (HasSavedFrame() && (aFrame != mWriteFrameStart))
{
uint16_t totalLength = LittleEndian::ReadUint16(aFrame + kHeaderTotalLengthOffset);
uint16_t skipLength = LittleEndian::ReadUint16(aFrame + kHeaderSkipLengthOffset);
diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp
index b41fde3..b250f09 100644
--- a/src/lib/spinel/radio_spinel.cpp
+++ b/src/lib/spinel/radio_spinel.cpp
@@ -46,6 +46,7 @@
#include "common/encoding.hpp"
#include "common/new.hpp"
#include "lib/platform/exit_code.h"
+#include "lib/spinel/logger.hpp"
#include "lib/spinel/spinel_decoder.hpp"
namespace ot {
@@ -81,7 +82,8 @@
}
RadioSpinel::RadioSpinel(void)
- : mInstance(nullptr)
+ : Logger("RadioSpinel")
+ , mInstance(nullptr)
, mSpinelInterface(nullptr)
, mCmdTidsInUse(0)
, mCmdNextTid(1)
@@ -670,6 +672,9 @@
ExitNow();
}
+ // this clear is necessary in case the RCP has sent messages between disable and reset
+ mRxFrameBuffer.Clear();
+
LogInfo("RCP reset: %s", spinel_status_to_cstr(status));
sIsReady = true;
}
@@ -2448,647 +2453,6 @@
}
#endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
-uint32_t RadioSpinel::Snprintf(char *aDest, uint32_t aSize, const char *aFormat, ...)
-{
- int len;
- va_list args;
-
- va_start(args, aFormat);
- len = vsnprintf(aDest, static_cast<size_t>(aSize), aFormat, args);
- va_end(args);
-
- return (len < 0) ? 0 : Min(static_cast<uint32_t>(len), aSize - 1);
-}
-
-void RadioSpinel::LogSpinelFrame(const uint8_t *aFrame, uint16_t aLength, bool aTx)
-{
- otError error = OT_ERROR_NONE;
- char buf[OPENTHREAD_CONFIG_LOG_MAX_SIZE] = {0};
- spinel_ssize_t unpacked;
- uint8_t header;
- uint32_t cmd;
- spinel_prop_key_t key;
- uint8_t *data;
- spinel_size_t len;
- const char *prefix = nullptr;
- char *start = buf;
- char *end = buf + sizeof(buf);
-
- VerifyOrExit(otLoggingGetLevel() >= OT_LOG_LEVEL_DEBG);
-
- prefix = aTx ? "Sent spinel frame" : "Received spinel frame";
- unpacked = spinel_datatype_unpack(aFrame, aLength, "CiiD", &header, &cmd, &key, &data, &len);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
- start += Snprintf(start, static_cast<uint32_t>(end - start), "%s, flg:0x%x, iid:%d, tid:%u, cmd:%s", prefix,
- SPINEL_HEADER_GET_FLAG(header), SPINEL_HEADER_GET_IID(header), SPINEL_HEADER_GET_TID(header),
- spinel_command_to_cstr(cmd));
- VerifyOrExit(cmd != SPINEL_CMD_RESET);
-
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", key:%s", spinel_prop_key_to_cstr(key));
- VerifyOrExit(cmd != SPINEL_CMD_PROP_VALUE_GET);
-
- switch (key)
- {
- case SPINEL_PROP_LAST_STATUS:
- {
- spinel_status_t status;
-
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &status);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", status:%s", spinel_status_to_cstr(status));
- }
- break;
-
- case SPINEL_PROP_MAC_RAW_STREAM_ENABLED:
- case SPINEL_PROP_MAC_SRC_MATCH_ENABLED:
- case SPINEL_PROP_PHY_ENABLED:
- case SPINEL_PROP_RADIO_COEX_ENABLE:
- {
- bool enabled;
-
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_BOOL_S, &enabled);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", enabled:%u", enabled);
- }
- break;
-
- case SPINEL_PROP_PHY_CCA_THRESHOLD:
- case SPINEL_PROP_PHY_FEM_LNA_GAIN:
- case SPINEL_PROP_PHY_RX_SENSITIVITY:
- case SPINEL_PROP_PHY_RSSI:
- case SPINEL_PROP_PHY_TX_POWER:
- {
- const char *name = nullptr;
- int8_t value;
-
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_INT8_S, &value);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
- switch (key)
- {
- case SPINEL_PROP_PHY_TX_POWER:
- name = "power";
- break;
- case SPINEL_PROP_PHY_CCA_THRESHOLD:
- name = "threshold";
- break;
- case SPINEL_PROP_PHY_FEM_LNA_GAIN:
- name = "gain";
- break;
- case SPINEL_PROP_PHY_RX_SENSITIVITY:
- name = "sensitivity";
- break;
- case SPINEL_PROP_PHY_RSSI:
- name = "rssi";
- break;
- }
-
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%d", name, value);
- }
- break;
-
- case SPINEL_PROP_MAC_PROMISCUOUS_MODE:
- case SPINEL_PROP_MAC_SCAN_STATE:
- case SPINEL_PROP_PHY_CHAN:
- case SPINEL_PROP_RCP_CSL_ACCURACY:
- case SPINEL_PROP_RCP_CSL_UNCERTAINTY:
- {
- const char *name = nullptr;
- uint8_t value;
-
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &value);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
- switch (key)
- {
- case SPINEL_PROP_MAC_SCAN_STATE:
- name = "state";
- break;
- case SPINEL_PROP_RCP_CSL_ACCURACY:
- name = "accuracy";
- break;
- case SPINEL_PROP_RCP_CSL_UNCERTAINTY:
- name = "uncertainty";
- break;
- case SPINEL_PROP_MAC_PROMISCUOUS_MODE:
- name = "mode";
- break;
- case SPINEL_PROP_PHY_CHAN:
- name = "channel";
- break;
- }
-
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
- }
- break;
-
- case SPINEL_PROP_MAC_15_4_PANID:
- case SPINEL_PROP_MAC_15_4_SADDR:
- case SPINEL_PROP_MAC_SCAN_PERIOD:
- case SPINEL_PROP_PHY_REGION_CODE:
- {
- const char *name = nullptr;
- uint16_t value;
-
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &value);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
- switch (key)
- {
- case SPINEL_PROP_MAC_SCAN_PERIOD:
- name = "period";
- break;
- case SPINEL_PROP_PHY_REGION_CODE:
- name = "region";
- break;
- case SPINEL_PROP_MAC_15_4_SADDR:
- name = "saddr";
- break;
- case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES:
- name = "saddr";
- break;
- case SPINEL_PROP_MAC_15_4_PANID:
- name = "panid";
- break;
- }
-
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:0x%04x", name, value);
- }
- break;
-
- case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES:
- {
- uint16_t saddr;
-
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", saddr:");
-
- if (len < sizeof(saddr))
- {
- start += Snprintf(start, static_cast<uint32_t>(end - start), "none");
- }
- else
- {
- while (len >= sizeof(saddr))
- {
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &saddr);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- data += unpacked;
- len -= static_cast<spinel_size_t>(unpacked);
- start += Snprintf(start, static_cast<uint32_t>(end - start), "0x%04x ", saddr);
- }
- }
- }
- break;
-
- case SPINEL_PROP_RCP_MAC_FRAME_COUNTER:
- case SPINEL_PROP_RCP_TIMESTAMP:
- {
- const char *name;
- uint32_t value;
-
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT32_S, &value);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
- name = (key == SPINEL_PROP_RCP_TIMESTAMP) ? "timestamp" : "counter";
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
- }
- break;
-
- case SPINEL_PROP_RADIO_CAPS:
- case SPINEL_PROP_RCP_API_VERSION:
- case SPINEL_PROP_RCP_MIN_HOST_API_VERSION:
- {
- const char *name;
- unsigned int value;
-
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &value);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
- switch (key)
- {
- case SPINEL_PROP_RADIO_CAPS:
- name = "caps";
- break;
- case SPINEL_PROP_RCP_API_VERSION:
- name = "version";
- break;
- case SPINEL_PROP_RCP_MIN_HOST_API_VERSION:
- name = "min-host-version";
- break;
- default:
- name = "";
- break;
- }
-
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%u", name, value);
- }
- break;
-
- case SPINEL_PROP_MAC_ENERGY_SCAN_RESULT:
- case SPINEL_PROP_PHY_CHAN_MAX_POWER:
- {
- const char *name;
- uint8_t channel;
- int8_t value;
-
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S, &channel, &value);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
- name = (key == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT) ? "rssi" : "power";
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", channel:%u, %s:%d", channel, name, value);
- }
- break;
-
- case SPINEL_PROP_CAPS:
- {
- unsigned int capability;
-
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", caps:");
-
- while (len > 0)
- {
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &capability);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- data += unpacked;
- len -= static_cast<spinel_size_t>(unpacked);
- start += Snprintf(start, static_cast<uint32_t>(end - start), "%s ", spinel_capability_to_cstr(capability));
- }
- }
- break;
-
- case SPINEL_PROP_PROTOCOL_VERSION:
- {
- unsigned int major;
- unsigned int minor;
-
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S,
- &major, &minor);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", major:%u, minor:%u", major, minor);
- }
- break;
-
- case SPINEL_PROP_PHY_CHAN_PREFERRED:
- case SPINEL_PROP_PHY_CHAN_SUPPORTED:
- {
- uint8_t maskBuffer[kChannelMaskBufferSize];
- uint32_t channelMask = 0;
- const uint8_t *maskData = maskBuffer;
- spinel_size_t maskLength = sizeof(maskBuffer);
-
- unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, maskBuffer, &maskLength);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
- while (maskLength > 0)
- {
- uint8_t channel;
-
- unpacked = spinel_datatype_unpack(maskData, maskLength, SPINEL_DATATYPE_UINT8_S, &channel);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- VerifyOrExit(channel < kChannelMaskBufferSize, error = OT_ERROR_PARSE);
- channelMask |= (1UL << channel);
-
- maskData += unpacked;
- maskLength -= static_cast<spinel_size_t>(unpacked);
- }
-
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", channelMask:0x%08x", channelMask);
- }
- break;
-
- case SPINEL_PROP_NCP_VERSION:
- {
- const char *version;
-
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &version);
- VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE);
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", version:%s", version);
- }
- break;
-
- case SPINEL_PROP_STREAM_RAW:
- {
- otRadioFrame frame;
-
- if (cmd == SPINEL_CMD_PROP_VALUE_IS)
- {
- uint16_t flags;
- int8_t noiseFloor;
- unsigned int receiveError;
-
- unpacked = spinel_datatype_unpack(data, len,
- SPINEL_DATATYPE_DATA_WLEN_S // Frame
- SPINEL_DATATYPE_INT8_S // RSSI
- SPINEL_DATATYPE_INT8_S // Noise Floor
- SPINEL_DATATYPE_UINT16_S // Flags
- SPINEL_DATATYPE_STRUCT_S( // PHY-data
- SPINEL_DATATYPE_UINT8_S // 802.15.4 channel
- SPINEL_DATATYPE_UINT8_S // 802.15.4 LQI
- SPINEL_DATATYPE_UINT64_S // Timestamp (us).
- ) SPINEL_DATATYPE_STRUCT_S( // Vendor-data
- SPINEL_DATATYPE_UINT_PACKED_S // Receive error
- ),
- &frame.mPsdu, &frame.mLength, &frame.mInfo.mRxInfo.mRssi, &noiseFloor,
- &flags, &frame.mChannel, &frame.mInfo.mRxInfo.mLqi,
- &frame.mInfo.mRxInfo.mTimestamp, &receiveError);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", len:%u, rssi:%d ...", frame.mLength,
- frame.mInfo.mRxInfo.mRssi);
- OT_UNUSED_VARIABLE(start); // Avoid static analysis error
- LogDebg("%s", buf);
-
- start = buf;
- start += Snprintf(start, static_cast<uint32_t>(end - start),
- "... noise:%d, flags:0x%04x, channel:%u, lqi:%u, timestamp:%lu, rxerr:%u", noiseFloor,
- flags, frame.mChannel, frame.mInfo.mRxInfo.mLqi,
- static_cast<unsigned long>(frame.mInfo.mRxInfo.mTimestamp), receiveError);
- }
- else if (cmd == SPINEL_CMD_PROP_VALUE_SET)
- {
- bool csmaCaEnabled;
- bool isHeaderUpdated;
- bool isARetx;
- bool skipAes;
-
- unpacked = spinel_datatype_unpack(
- data, len,
- SPINEL_DATATYPE_DATA_WLEN_S // Frame data
- SPINEL_DATATYPE_UINT8_S // Channel
- SPINEL_DATATYPE_UINT8_S // MaxCsmaBackoffs
- SPINEL_DATATYPE_UINT8_S // MaxFrameRetries
- SPINEL_DATATYPE_BOOL_S // CsmaCaEnabled
- SPINEL_DATATYPE_BOOL_S // IsHeaderUpdated
- SPINEL_DATATYPE_BOOL_S // IsARetx
- SPINEL_DATATYPE_BOOL_S // SkipAes
- SPINEL_DATATYPE_UINT32_S // TxDelay
- SPINEL_DATATYPE_UINT32_S, // TxDelayBaseTime
- &frame.mPsdu, &frame.mLength, &frame.mChannel, &frame.mInfo.mTxInfo.mMaxCsmaBackoffs,
- &frame.mInfo.mTxInfo.mMaxFrameRetries, &csmaCaEnabled, &isHeaderUpdated, &isARetx, &skipAes,
- &frame.mInfo.mTxInfo.mTxDelay, &frame.mInfo.mTxInfo.mTxDelayBaseTime);
-
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- start += Snprintf(start, static_cast<uint32_t>(end - start),
- ", len:%u, channel:%u, maxbackoffs:%u, maxretries:%u ...", frame.mLength, frame.mChannel,
- frame.mInfo.mTxInfo.mMaxCsmaBackoffs, frame.mInfo.mTxInfo.mMaxFrameRetries);
- OT_UNUSED_VARIABLE(start); // Avoid static analysis error
- LogDebg("%s", buf);
-
- start = buf;
- start += Snprintf(start, static_cast<uint32_t>(end - start),
- "... csmaCaEnabled:%u, isHeaderUpdated:%u, isARetx:%u, skipAes:%u"
- ", txDelay:%u, txDelayBase:%u",
- csmaCaEnabled, isHeaderUpdated, isARetx, skipAes, frame.mInfo.mTxInfo.mTxDelay,
- frame.mInfo.mTxInfo.mTxDelayBaseTime);
- }
- }
- break;
-
- case SPINEL_PROP_STREAM_DEBUG:
- {
- char debugString[OPENTHREAD_CONFIG_NCP_SPINEL_LOG_MAX_SIZE + 1];
- spinel_size_t stringLength = sizeof(debugString);
-
- unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, debugString, &stringLength);
- assert(stringLength < sizeof(debugString));
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- debugString[stringLength] = '\0';
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", debug:%s", debugString);
- }
- break;
-
- case SPINEL_PROP_STREAM_LOG:
- {
- const char *logString;
- uint8_t logLevel;
-
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &logString);
- VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE);
- data += unpacked;
- len -= static_cast<spinel_size_t>(unpacked);
-
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &logLevel);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", level:%u, log:%s", logLevel, logString);
- }
- break;
-
- case SPINEL_PROP_NEST_STREAM_MFG:
- {
- const char *output;
- size_t outputLen;
-
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &output, &outputLen);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", diag:%s", output);
- }
- break;
-
- case SPINEL_PROP_RCP_MAC_KEY:
- {
- uint8_t keyIdMode;
- uint8_t keyId;
- otMacKey prevKey;
- unsigned int prevKeyLen = sizeof(otMacKey);
- otMacKey currKey;
- unsigned int currKeyLen = sizeof(otMacKey);
- otMacKey nextKey;
- unsigned int nextKeyLen = sizeof(otMacKey);
-
- unpacked = spinel_datatype_unpack(data, len,
- SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_DATA_WLEN_S
- SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_WLEN_S,
- &keyIdMode, &keyId, prevKey.m8, &prevKeyLen, currKey.m8, &currKeyLen,
- nextKey.m8, &nextKeyLen);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- start += Snprintf(start, static_cast<uint32_t>(end - start),
- ", keyIdMode:%u, keyId:%u, prevKey:***, currKey:***, nextKey:***", keyIdMode, keyId);
- }
- break;
-
- case SPINEL_PROP_HWADDR:
- case SPINEL_PROP_MAC_15_4_LADDR:
- {
- const char *name = nullptr;
- uint8_t m8[OT_EXT_ADDRESS_SIZE] = {0};
-
- unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, &m8[0]);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
- name = (key == SPINEL_PROP_HWADDR) ? "eui64" : "laddr";
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", %s:%02x%02x%02x%02x%02x%02x%02x%02x", name,
- m8[0], m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]);
- }
- break;
-
- case SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES:
- {
- uint8_t m8[OT_EXT_ADDRESS_SIZE];
-
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", extaddr:");
-
- if (len < sizeof(m8))
- {
- start += Snprintf(start, static_cast<uint32_t>(end - start), "none");
- }
- else
- {
- while (len >= sizeof(m8))
- {
- unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, m8);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- data += unpacked;
- len -= static_cast<spinel_size_t>(unpacked);
- start += Snprintf(start, static_cast<uint32_t>(end - start), "%02x%02x%02x%02x%02x%02x%02x%02x ", m8[0],
- m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]);
- }
- }
- }
- break;
-
- case SPINEL_PROP_RADIO_COEX_METRICS:
- {
- otRadioCoexMetrics metrics;
- unpacked = spinel_datatype_unpack(
- data, len,
- SPINEL_DATATYPE_STRUCT_S( // Tx Coex Metrics Structure
- SPINEL_DATATYPE_UINT32_S // NumTxRequest
- SPINEL_DATATYPE_UINT32_S // NumTxGrantImmediate
- SPINEL_DATATYPE_UINT32_S // NumTxGrantWait
- SPINEL_DATATYPE_UINT32_S // NumTxGrantWaitActivated
- SPINEL_DATATYPE_UINT32_S // NumTxGrantWaitTimeout
- SPINEL_DATATYPE_UINT32_S // NumTxGrantDeactivatedDuringRequest
- SPINEL_DATATYPE_UINT32_S // NumTxDelayedGrant
- SPINEL_DATATYPE_UINT32_S // AvgTxRequestToGrantTime
- ) SPINEL_DATATYPE_STRUCT_S( // Rx Coex Metrics Structure
- SPINEL_DATATYPE_UINT32_S // NumRxRequest
- SPINEL_DATATYPE_UINT32_S // NumRxGrantImmediate
- SPINEL_DATATYPE_UINT32_S // NumRxGrantWait
- SPINEL_DATATYPE_UINT32_S // NumRxGrantWaitActivated
- SPINEL_DATATYPE_UINT32_S // NumRxGrantWaitTimeout
- SPINEL_DATATYPE_UINT32_S // NumRxGrantDeactivatedDuringRequest
- SPINEL_DATATYPE_UINT32_S // NumRxDelayedGrant
- SPINEL_DATATYPE_UINT32_S // AvgRxRequestToGrantTime
- SPINEL_DATATYPE_UINT32_S // NumRxGrantNone
- ) SPINEL_DATATYPE_BOOL_S // Stopped
- SPINEL_DATATYPE_UINT32_S, // NumGrantGlitch
- &metrics.mNumTxRequest, &metrics.mNumTxGrantImmediate, &metrics.mNumTxGrantWait,
- &metrics.mNumTxGrantWaitActivated, &metrics.mNumTxGrantWaitTimeout,
- &metrics.mNumTxGrantDeactivatedDuringRequest, &metrics.mNumTxDelayedGrant,
- &metrics.mAvgTxRequestToGrantTime, &metrics.mNumRxRequest, &metrics.mNumRxGrantImmediate,
- &metrics.mNumRxGrantWait, &metrics.mNumRxGrantWaitActivated, &metrics.mNumRxGrantWaitTimeout,
- &metrics.mNumRxGrantDeactivatedDuringRequest, &metrics.mNumRxDelayedGrant,
- &metrics.mAvgRxRequestToGrantTime, &metrics.mNumRxGrantNone, &metrics.mStopped, &metrics.mNumGrantGlitch);
-
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
- LogDebg("%s ...", buf);
- LogDebg(" txRequest:%lu", ToUlong(metrics.mNumTxRequest));
- LogDebg(" txGrantImmediate:%lu", ToUlong(metrics.mNumTxGrantImmediate));
- LogDebg(" txGrantWait:%lu", ToUlong(metrics.mNumTxGrantWait));
- LogDebg(" txGrantWaitActivated:%lu", ToUlong(metrics.mNumTxGrantWaitActivated));
- LogDebg(" txGrantWaitTimeout:%lu", ToUlong(metrics.mNumTxGrantWaitTimeout));
- LogDebg(" txGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumTxGrantDeactivatedDuringRequest));
- LogDebg(" txDelayedGrant:%lu", ToUlong(metrics.mNumTxDelayedGrant));
- LogDebg(" avgTxRequestToGrantTime:%lu", ToUlong(metrics.mAvgTxRequestToGrantTime));
- LogDebg(" rxRequest:%lu", ToUlong(metrics.mNumRxRequest));
- LogDebg(" rxGrantImmediate:%lu", ToUlong(metrics.mNumRxGrantImmediate));
- LogDebg(" rxGrantWait:%lu", ToUlong(metrics.mNumRxGrantWait));
- LogDebg(" rxGrantWaitActivated:%lu", ToUlong(metrics.mNumRxGrantWaitActivated));
- LogDebg(" rxGrantWaitTimeout:%lu", ToUlong(metrics.mNumRxGrantWaitTimeout));
- LogDebg(" rxGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumRxGrantDeactivatedDuringRequest));
- LogDebg(" rxDelayedGrant:%lu", ToUlong(metrics.mNumRxDelayedGrant));
- LogDebg(" avgRxRequestToGrantTime:%lu", ToUlong(metrics.mAvgRxRequestToGrantTime));
- LogDebg(" rxGrantNone:%lu", ToUlong(metrics.mNumRxGrantNone));
- LogDebg(" stopped:%u", metrics.mStopped);
-
- start = buf;
- start += Snprintf(start, static_cast<uint32_t>(end - start), " grantGlitch:%u", metrics.mNumGrantGlitch);
- }
- break;
-
- case SPINEL_PROP_MAC_SCAN_MASK:
- {
- constexpr uint8_t kNumChannels = 16;
- uint8_t channels[kNumChannels];
- spinel_size_t size;
-
- unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_DATA_S, channels, &size);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", channels:");
-
- for (spinel_size_t i = 0; i < size; i++)
- {
- start += Snprintf(start, static_cast<uint32_t>(end - start), "%u ", channels[i]);
- }
- }
- break;
-
- case SPINEL_PROP_RCP_ENH_ACK_PROBING:
- {
- uint16_t saddr;
- uint8_t m8[OT_EXT_ADDRESS_SIZE];
- uint8_t flags;
-
- unpacked = spinel_datatype_unpack(
- data, len, SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT8_S, &saddr, m8, &flags);
-
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- start += Snprintf(start, static_cast<uint32_t>(end - start),
- ", saddr:%04x, extaddr:%02x%02x%02x%02x%02x%02x%02x%02x, flags:0x%02x", saddr, m8[0], m8[1],
- m8[2], m8[3], m8[4], m8[5], m8[6], m8[7], flags);
- }
- break;
-
- case SPINEL_PROP_PHY_CALIBRATED_POWER:
- {
- if (cmd == SPINEL_CMD_PROP_VALUE_INSERT)
- {
- uint8_t channel;
- int16_t actualPower;
- uint8_t *rawPowerSetting;
- unsigned int rawPowerSettingLength;
-
- unpacked = spinel_datatype_unpack(
- data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_DATA_WLEN_S, &channel,
- &actualPower, &rawPowerSetting, &rawPowerSettingLength);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
-
- start += Snprintf(start, static_cast<uint32_t>(end - start),
- ", ch:%u, actualPower:%d, rawPowerSetting:", channel, actualPower);
- for (unsigned int i = 0; i < rawPowerSettingLength; i++)
- {
- start += Snprintf(start, static_cast<uint32_t>(end - start), "%02x", rawPowerSetting[i]);
- }
- }
- }
- break;
-
- case SPINEL_PROP_PHY_CHAN_TARGET_POWER:
- {
- uint8_t channel;
- int16_t targetPower;
-
- unpacked =
- spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S, &channel, &targetPower);
- VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
- start += Snprintf(start, static_cast<uint32_t>(end - start), ", ch:%u, targetPower:%d", channel, targetPower);
- }
- break;
- }
-
-exit:
- OT_UNUSED_VARIABLE(start); // Avoid static analysis error
- if (error == OT_ERROR_NONE)
- {
- LogDebg("%s", buf);
- }
- else if (prefix != nullptr)
- {
- LogDebg("%s, failed to parse spinel frame !", prefix);
- }
-}
-
otError RadioSpinel::SpinelStatusToOtError(spinel_status_t aStatus)
{
otError ret;
@@ -3166,62 +2530,5 @@
return ret;
}
-void RadioSpinel::LogIfFail(const char *aText, otError aError)
-{
- OT_UNUSED_VARIABLE(aText);
-
- if (aError != OT_ERROR_NONE && aError != OT_ERROR_NO_ACK)
- {
- LogWarn("%s: %s", aText, otThreadErrorToString(aError));
- }
-}
-
-static const char kModuleName[] = "RadioSpinel";
-
-void RadioSpinel::LogCrit(const char *aFormat, ...)
-{
- va_list args;
-
- va_start(args, aFormat);
- otLogPlatArgs(OT_LOG_LEVEL_CRIT, kModuleName, aFormat, args);
- va_end(args);
-}
-
-void RadioSpinel::LogWarn(const char *aFormat, ...)
-{
- va_list args;
-
- va_start(args, aFormat);
- otLogPlatArgs(OT_LOG_LEVEL_WARN, kModuleName, aFormat, args);
- va_end(args);
-}
-
-void RadioSpinel::LogNote(const char *aFormat, ...)
-{
- va_list args;
-
- va_start(args, aFormat);
- otLogPlatArgs(OT_LOG_LEVEL_NOTE, kModuleName, aFormat, args);
- va_end(args);
-}
-
-void RadioSpinel::LogInfo(const char *aFormat, ...)
-{
- va_list args;
-
- va_start(args, aFormat);
- otLogPlatArgs(OT_LOG_LEVEL_INFO, kModuleName, aFormat, args);
- va_end(args);
-}
-
-void RadioSpinel::LogDebg(const char *aFormat, ...)
-{
- va_list args;
-
- va_start(args, aFormat);
- otLogPlatArgs(OT_LOG_LEVEL_DEBG, kModuleName, aFormat, args);
- va_end(args);
-}
-
} // namespace Spinel
} // namespace ot
diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp
index 7cdd1f8..5f55c75 100644
--- a/src/lib/spinel/radio_spinel.hpp
+++ b/src/lib/spinel/radio_spinel.hpp
@@ -38,6 +38,7 @@
#include "openthread-spinel-config.h"
#include "core/radio/max_power_table.hpp"
+#include "lib/spinel/logger.hpp"
#include "lib/spinel/radio_spinel_metrics.h"
#include "lib/spinel/spinel.h"
#include "lib/spinel/spinel_interface.hpp"
@@ -148,7 +149,7 @@
* co-processor(RCP).
*
*/
-class RadioSpinel
+class RadioSpinel : private Logger
{
public:
/**
@@ -1238,17 +1239,6 @@
static otError ReadMacKey(const otMacKeyMaterial &aKeyMaterial, otMacKey &aKey);
#endif
- static void LogIfFail(const char *aText, otError aError);
-
- static void LogCrit(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2);
- static void LogWarn(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2);
- static void LogNote(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2);
- static void LogInfo(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2);
- static void LogDebg(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2);
-
- uint32_t Snprintf(char *aDest, uint32_t aSize, const char *aFormat, ...);
- void LogSpinelFrame(const uint8_t *aFrame, uint16_t aLength, bool aTx);
-
otInstance *mInstance;
SpinelInterface::RxFrameBuffer mRxFrameBuffer;
@@ -1297,8 +1287,7 @@
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
- enum
- {
+ enum {
kRcpFailureNone,
kRcpFailureTimeout,
kRcpFailureUnexpectedReset,
diff --git a/src/lib/spinel/spinel_encoder.cpp b/src/lib/spinel/spinel_encoder.cpp
index 91a9ec3..6cd3904 100644
--- a/src/lib/spinel/spinel_encoder.cpp
+++ b/src/lib/spinel/spinel_encoder.cpp
@@ -291,5 +291,7 @@
return error;
}
+void Encoder::ClearNcpBuffer(void) { mNcpBuffer.Clear(); }
+
} // namespace Spinel
} // namespace ot
diff --git a/src/lib/spinel/spinel_encoder.hpp b/src/lib/spinel/spinel_encoder.hpp
index 6b9ea98..c678519 100644
--- a/src/lib/spinel/spinel_encoder.hpp
+++ b/src/lib/spinel/spinel_encoder.hpp
@@ -676,6 +676,12 @@
*/
otError ResetToSaved(void);
+ /**
+ * Clear NCP buffer on reset command.
+ *
+ */
+ void ClearNcpBuffer(void);
+
private:
enum
{
diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp
index e0b4c18..a1ad3ad 100644
--- a/src/ncp/ncp_base.cpp
+++ b/src/ncp/ncp_base.cpp
@@ -1285,6 +1285,8 @@
ResetCounters();
+ mEncoder.ClearNcpBuffer();
+
SuccessOrAssert(error = WriteLastStatusFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID,
SPINEL_STATUS_RESET_POWER_ON));
}
diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp
index badc6a5..aa87efa 100644
--- a/src/ncp/ncp_base_mtd.cpp
+++ b/src/ncp/ncp_base_mtd.cpp
@@ -690,7 +690,7 @@
SuccessOrExit(error = mDecoder.ReadUint32(keyGuardTime));
- otThreadSetKeySwitchGuardTime(mInstance, keyGuardTime);
+ otThreadSetKeySwitchGuardTime(mInstance, static_cast<uint16_t>(keyGuardTime));
exit:
return error;
diff --git a/src/posix/platform/config_file.hpp b/src/posix/platform/config_file.hpp
index 51d16ca..1590ed2 100644
--- a/src/posix/platform/config_file.hpp
+++ b/src/posix/platform/config_file.hpp
@@ -26,8 +26,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef POSIX_PLATFORM_CONFIG_FILE_HPP_
-#define POSIX_PLATFORM_CONFIG_FILE_HPP_
+#ifndef OT_POSIX_PLATFORM_CONFIG_FILE_HPP_
+#define OT_POSIX_PLATFORM_CONFIG_FILE_HPP_
#include <assert.h>
#include <stdint.h>
@@ -125,4 +125,4 @@
} // namespace Posix
} // namespace ot
-#endif // POSIX_PLATFORM_CONFIG_FILE_HPP_
+#endif // OT_POSIX_PLATFORM_CONFIG_FILE_HPP_
diff --git a/src/posix/platform/configuration.cpp b/src/posix/platform/configuration.cpp
index 07f1208..6311bdc 100644
--- a/src/posix/platform/configuration.cpp
+++ b/src/posix/platform/configuration.cpp
@@ -43,6 +43,8 @@
namespace ot {
namespace Posix {
+const char Configuration::kLogModuleName[] = "Config";
+
#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE
const char Configuration::kKeyCalibratedPower[] = "calibrated_power";
#endif
@@ -74,12 +76,12 @@
exit:
if (error == OT_ERROR_NONE)
{
- otLogInfoPlat("Successfully set region \"%c%c\"", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff));
+ LogInfo("Successfully set region \"%c%c\"", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff));
}
else
{
- otLogCritPlat("Failed to set region \"%c%c\": %s", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff),
- otThreadErrorToString(error));
+ LogCrit("Failed to set region \"%c%c\": %s", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff),
+ otThreadErrorToString(error));
}
return error;
@@ -112,7 +114,7 @@
exit:
if (error != OT_ERROR_NONE)
{
- otLogCritPlat("Failed to get power domain: %s", otThreadErrorToString(error));
+ LogCrit("Failed to get power domain: %s", otThreadErrorToString(error));
}
return error;
@@ -165,7 +167,7 @@
exit:
if (error != OT_ERROR_NONE)
{
- otLogCritPlat("Failed to update channel mask: %s", otThreadErrorToString(error));
+ LogCrit("Failed to update channel mask: %s", otThreadErrorToString(error));
}
return error;
@@ -182,7 +184,7 @@
while (GetNextTargetPower(aDomain, iterator, targetPower) == OT_ERROR_NONE)
{
- otLogInfoPlat("Update target power: %s\r\n", targetPower.ToString().AsCString());
+ LogInfo("Update target power: %s\r\n", targetPower.ToString().AsCString());
for (uint8_t ch = targetPower.GetChannelStart(); ch <= targetPower.GetChannelEnd(); ch++)
{
@@ -193,7 +195,7 @@
exit:
if (error != OT_ERROR_NONE)
{
- otLogCritPlat("Failed to update target power: %s", otThreadErrorToString(error));
+ LogCrit("Failed to update target power: %s", otThreadErrorToString(error));
}
return error;
@@ -222,7 +224,7 @@
while (calibrationFile->Get(kKeyCalibratedPower, iterator, value, sizeof(value)) == OT_ERROR_NONE)
{
SuccessOrExit(error = calibratedPower.FromString(value));
- otLogInfoPlat("Update calibrated power: %s\r\n", calibratedPower.ToString().AsCString());
+ LogInfo("Update calibrated power: %s\r\n", calibratedPower.ToString().AsCString());
for (uint8_t ch = calibratedPower.GetChannelStart(); ch <= calibratedPower.GetChannelEnd(); ch++)
{
@@ -235,7 +237,7 @@
exit:
if (error != OT_ERROR_NONE)
{
- otLogCritPlat("Failed to update calibrated power table: %s", otThreadErrorToString(error));
+ LogCrit("Failed to update calibrated power table: %s", otThreadErrorToString(error));
}
return error;
@@ -259,7 +261,7 @@
if ((error = aTargetPower.FromString(psave)) != OT_ERROR_NONE)
{
- otLogCritPlat("Failed to read target power: %s", otThreadErrorToString(error));
+ LogCrit("Failed to read target power: %s", otThreadErrorToString(error));
}
break;
}
diff --git a/src/posix/platform/configuration.hpp b/src/posix/platform/configuration.hpp
index 71c479d..e2a2942 100644
--- a/src/posix/platform/configuration.hpp
+++ b/src/posix/platform/configuration.hpp
@@ -26,8 +26,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef POSIX_PLATFORM_CONFIGURATION_HPP_
-#define POSIX_PLATFORM_CONFIGURATION_HPP_
+#ifndef OT_POSIX_PLATFORM_CONFIGURATION_HPP_
+#define OT_POSIX_PLATFORM_CONFIGURATION_HPP_
#include "openthread-posix-config.h"
@@ -41,7 +41,9 @@
#include <openthread/platform/radio.h>
#include "config_file.hpp"
+#include "logger.hpp"
#include "power.hpp"
+
#include "common/code_utils.hpp"
namespace ot {
@@ -51,9 +53,11 @@
* Updates the target power table and calibrated power table to the RCP.
*
*/
-class Configuration
+class Configuration : public Logger<Configuration>
{
public:
+ static const char kLogModuleName[]; ///< Module name used for logging.
+
Configuration(void)
: mFactoryConfigFile(OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE)
, mProductConfigFile(OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE)
@@ -150,4 +154,4 @@
} // namespace ot
#endif // OPENTHREAD_POSIX_CONFIG_CONFIGURATION_FILE_ENABLE
-#endif // POSIX_PLATFORM_CONFIGURATION_HPP_
+#endif // OT_POSIX_PLATFORM_CONFIGURATION_HPP_
diff --git a/src/posix/platform/daemon.cpp b/src/posix/platform/daemon.cpp
index 14c40cf..1436e45 100644
--- a/src/posix/platform/daemon.cpp
+++ b/src/posix/platform/daemon.cpp
@@ -75,6 +75,8 @@
} // namespace
+const char Daemon::kLogModuleName[] = "Daemon";
+
int Daemon::OutputFormat(const char *aFormat, ...)
{
int ret;
@@ -97,7 +99,7 @@
"OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH is too short!");
rval = vsnprintf(buf, sizeof(buf), aFormat, aArguments);
- VerifyOrExit(rval >= 0, otLogWarnPlat("Failed to format CLI output: %s", strerror(errno)));
+ VerifyOrExit(rval >= 0, LogWarn("Failed to format CLI output: %s", strerror(errno)));
if (rval >= static_cast<int>(sizeof(buf)))
{
@@ -116,7 +118,7 @@
if (rval < 0)
{
- otLogWarnPlat("Failed to write CLI output: %s", strerror(errno));
+ LogWarn("Failed to write CLI output: %s", strerror(errno));
close(mSessionSocket);
mSessionSocket = -1;
}
@@ -160,7 +162,7 @@
exit:
if (rval == -1)
{
- otLogWarnPlat("Failed to initialize session socket: %s", strerror(errno));
+ LogWarn("Failed to initialize session socket: %s", strerror(errno));
if (newSessionSocket != -1)
{
close(newSessionSocket);
@@ -168,7 +170,7 @@
}
else
{
- otLogInfoPlat("Session socket is ready");
+ LogInfo("Session socket is ready");
}
}
@@ -318,7 +320,7 @@
Filename sockfile;
GetFilename(sockfile, OPENTHREAD_POSIX_DAEMON_SOCKET_NAME);
- otLogDebgPlat("Removing daemon socket: %s", sockfile);
+ LogDebg("Removing daemon socket: %s", sockfile);
(void)unlink(sockfile);
}
@@ -400,7 +402,7 @@
{
if (rval < 0)
{
- otLogWarnPlat("Daemon read: %s", strerror(errno));
+ LogWarn("Daemon read: %s", strerror(errno));
}
close(mSessionSocket);
mSessionSocket = -1;
diff --git a/src/posix/platform/daemon.hpp b/src/posix/platform/daemon.hpp
index 0d40511..be3738b 100644
--- a/src/posix/platform/daemon.hpp
+++ b/src/posix/platform/daemon.hpp
@@ -31,14 +31,18 @@
#include "openthread-posix-config.h"
#include "core/common/non_copyable.hpp"
-#include "posix/platform/mainloop.hpp"
+
+#include "logger.hpp"
+#include "mainloop.hpp"
namespace ot {
namespace Posix {
-class Daemon : public Mainloop::Source, private NonCopyable
+class Daemon : public Mainloop::Source, public Logger<Daemon>, private NonCopyable
{
public:
+ static const char kLogModuleName[];
+
static Daemon &Get(void);
void SetUp(void);
diff --git a/src/posix/platform/firewall.cpp b/src/posix/platform/firewall.cpp
index 19ad8c5..7e9d47e 100644
--- a/src/posix/platform/firewall.cpp
+++ b/src/posix/platform/firewall.cpp
@@ -124,7 +124,7 @@
exit:
if (error != OT_ERROR_NONE)
{
- otLogWarnPlat("Failed to update ipsets: %s", otThreadErrorToString(error));
+ otLogWarnPlat("Firewall - failed to update ipsets: %s", otThreadErrorToString(error));
}
}
diff --git a/src/posix/platform/hdlc_interface.cpp b/src/posix/platform/hdlc_interface.cpp
index d2c45e4..12726fc 100644
--- a/src/posix/platform/hdlc_interface.cpp
+++ b/src/posix/platform/hdlc_interface.cpp
@@ -128,6 +128,8 @@
namespace ot {
namespace Posix {
+const char HdlcInterface::kLogModuleName[] = "HdlcIntface";
+
HdlcInterface::HdlcInterface(const Url::Url &aRadioUrl)
: mReceiveFrameCallback(nullptr)
, mReceiveFrameContext(nullptr)
@@ -164,7 +166,7 @@
#endif // OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
else
{
- otLogCritPlat("Radio file '%s' not supported", mRadioUrl.GetPath());
+ LogCrit("Radio file '%s' not supported", mRadioUrl.GetPath());
ExitNow(error = OT_ERROR_FAILED);
}
@@ -714,7 +716,7 @@
{
mInterfaceMetrics.mTransferredGarbageFrameCount++;
mReceiveFrameBuffer->DiscardFrame();
- otLogWarnPlat("Error decoding hdlc frame: %s", otThreadErrorToString(aError));
+ LogWarn("Error decoding hdlc frame: %s", otThreadErrorToString(aError));
}
exit:
@@ -742,7 +744,7 @@
usleep(static_cast<useconds_t>(kOpenFileDelay) * US_PER_MS);
} while (end > otPlatTimeGet());
- otLogCritPlat("Failed to reopen UART connection after resetting the RCP device.");
+ LogCrit("Failed to reopen UART connection after resetting the RCP device.");
error = OT_ERROR_FAILED;
}
diff --git a/src/posix/platform/hdlc_interface.hpp b/src/posix/platform/hdlc_interface.hpp
index 6078135..c6e01c7 100644
--- a/src/posix/platform/hdlc_interface.hpp
+++ b/src/posix/platform/hdlc_interface.hpp
@@ -31,9 +31,10 @@
* This file includes definitions for the HDLC interface to radio (RCP).
*/
-#ifndef POSIX_PLATFORM_HDLC_INTERFACE_HPP_
-#define POSIX_PLATFORM_HDLC_INTERFACE_HPP_
+#ifndef OT_POSIX_PLATFORM_HDLC_INTERFACE_HPP_
+#define OT_POSIX_PLATFORM_HDLC_INTERFACE_HPP_
+#include "logger.hpp"
#include "openthread-posix-config.h"
#include "platform-posix.h"
#include "lib/hdlc/hdlc.hpp"
@@ -48,9 +49,11 @@
* Defines an HDLC interface to the Radio Co-processor (RCP)
*
*/
-class HdlcInterface : public ot::Spinel::SpinelInterface
+class HdlcInterface : public ot::Spinel::SpinelInterface, public Logger<HdlcInterface>
{
public:
+ static const char kLogModuleName[]; ///< Module name used for logging.
+
/**
* Initializes the object.
*
@@ -272,4 +275,5 @@
} // namespace Posix
} // namespace ot
-#endif // POSIX_PLATFORM_HDLC_INTERFACE_HPP_
+
+#endif // OT_POSIX_PLATFORM_HDLC_INTERFACE_HPP_
diff --git a/src/posix/platform/infra_if.cpp b/src/posix/platform/infra_if.cpp
index 6e16b3f..0c97852 100644
--- a/src/posix/platform/infra_if.cpp
+++ b/src/posix/platform/infra_if.cpp
@@ -128,6 +128,8 @@
namespace ot {
namespace Posix {
+const char InfraNetif::kLogModuleName[] = "InfraNetif";
+
int InfraNetif::CreateIcmp6Socket(const char *aInfraIfName)
{
int sock;
@@ -174,7 +176,7 @@
#ifdef __linux__
rval = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, aInfraIfName, strlen(aInfraIfName));
#else // __NetBSD__ || __FreeBSD__ || __APPLE__
- rval = setsockopt(mInfraIfIcmp6Socket, IPPROTO_IPV6, IPV6_BOUND_IF, &mInfraIfIndex, sizeof(mInfraIfIndex));
+ rval = setsockopt(sock, IPPROTO_IPV6, IPV6_BOUND_IF, aInfraIfName, strlen(aInfraIfName));
#endif // __linux__
VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
@@ -190,6 +192,7 @@
bool IsAddressGlobalUnicast(const in6_addr &aAddress) { return (aAddress.s6_addr[0] & 0xe0) == 0x20; }
+#ifdef __linux__
// Create a net-link socket that subscribes to link & addresses events.
int CreateNetLinkSocket(void)
{
@@ -209,6 +212,7 @@
return sock;
}
+#endif // #ifdef __linux__
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
otError InfraNetif::SendIcmp6Nd(uint32_t aInfraIfIndex,
@@ -269,15 +273,16 @@
memcpy(CMSG_DATA(cmsgPointer), &hopLimit, sizeof(hopLimit));
rval = sendmsg(mInfraIfIcmp6Socket, &msgHeader, 0);
+
if (rval < 0)
{
- otLogWarnPlat("failed to send ICMPv6 message: %s", strerror(errno));
+ LogWarn("failed to send ICMPv6 message: %s", strerror(errno));
ExitNow(error = OT_ERROR_FAILED);
}
if (static_cast<size_t>(rval) != iov.iov_len)
{
- otLogWarnPlat("failed to send ICMPv6 message: partially sent");
+ LogWarn("failed to send ICMPv6 message: partially sent");
ExitNow(error = OT_ERROR_FAILED);
}
@@ -309,7 +314,7 @@
if (ioctl(sock, SIOCGIFFLAGS, &ifReq) == -1)
{
#if OPENTHREAD_POSIX_CONFIG_EXIT_ON_INFRA_NETIF_LOST_ENABLE
- otLogCritPlat("The infra link %s may be lost. Exiting.", mInfraIfName);
+ LogCrit("The infra link %s may be lost. Exiting.", mInfraIfName);
DieNow(OT_EXIT_ERROR_ERRNO);
#endif
ExitNow();
@@ -332,7 +337,7 @@
if (getifaddrs(&ifAddrs) < 0)
{
- otLogWarnPlat("failed to get netif addresses: %s", strerror(errno));
+ LogWarn("failed to get netif addresses: %s", strerror(errno));
ExitNow();
}
@@ -379,7 +384,7 @@
if (getifaddrs(&ifAddrs) < 0)
{
- otLogCritPlat("failed to get netif addresses: %s", strerror(errno));
+ LogCrit("failed to get netif addresses: %s", strerror(errno));
DieNow(OT_EXIT_ERROR_ERRNO);
}
@@ -405,7 +410,12 @@
return hasLla;
}
-void InfraNetif::Init(void) { mNetLinkSocket = CreateNetLinkSocket(); }
+void InfraNetif::Init(void)
+{
+#ifdef __linux__
+ mNetLinkSocket = CreateNetLinkSocket();
+#endif
+}
void InfraNetif::SetInfraNetif(const char *aIfName, int aIcmp6Socket)
{
@@ -414,7 +424,9 @@
OT_UNUSED_VARIABLE(aIcmp6Socket);
OT_ASSERT(gInstance != nullptr);
+#ifdef __linux__
VerifyOrDie(mNetLinkSocket != -1, OT_EXIT_INVALID_STATE);
+#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
SetInfraNetifIcmp6SocketForBorderRouting(aIcmp6Socket);
@@ -425,7 +437,7 @@
if (aIfName == nullptr || aIfName[0] == '\0')
{
- otLogWarnPlat("Border Routing/Backbone Router feature is disabled: infra interface is missing");
+ LogWarn("Border Routing/Backbone Router feature is disabled: infra interface is missing");
ExitNow();
}
@@ -436,7 +448,7 @@
ifIndex = if_nametoindex(aIfName);
if (ifIndex == 0)
{
- otLogCritPlat("Failed to get the index for infra interface %s", aIfName);
+ LogCrit("Failed to get the index for infra interface %s", aIfName);
DieNow(OT_EXIT_INVALID_ARGUMENTS);
}
@@ -449,7 +461,9 @@
void InfraNetif::SetUp(void)
{
OT_ASSERT(gInstance != nullptr);
+#ifdef __linux__
VerifyOrExit(mNetLinkSocket != -1);
+#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
SuccessOrDie(otBorderRoutingInit(gInstance, mInfraIfIndex, otSysInfraIfIsRunning()));
@@ -461,6 +475,9 @@
#endif
Mainloop::Manager::Get().Add(*this);
+
+ ExitNow(); // To silence unused `exit` label warning.
+
exit:
return;
}
@@ -488,11 +505,13 @@
}
#endif
+#ifdef __linux__
if (mNetLinkSocket != -1)
{
close(mNetLinkSocket);
mNetLinkSocket = -1;
}
+#endif
mInfraIfName[0] = '\0';
mInfraIfIndex = 0;
@@ -500,7 +519,9 @@
void InfraNetif::Update(otSysMainloopContext &aContext)
{
+#ifdef __linux__
VerifyOrExit(mNetLinkSocket != -1);
+#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
VerifyOrExit(mInfraIfIcmp6Socket != -1);
@@ -509,13 +530,17 @@
aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mInfraIfIcmp6Socket);
#endif
+#ifdef __linux__
FD_SET(mNetLinkSocket, &aContext.mReadFdSet);
aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mNetLinkSocket);
+#endif
exit:
return;
}
+#ifdef __linux__
+
void InfraNetif::ReceiveNetLinkMessage(void)
{
const size_t kMaxNetLinkBufSize = 8192;
@@ -529,7 +554,7 @@
len = recv(mNetLinkSocket, msgBuffer.mBuffer, sizeof(msgBuffer.mBuffer), 0);
if (len < 0)
{
- otLogCritPlat("Failed to receive netlink message: %s", strerror(errno));
+ LogCrit("Failed to receive netlink message: %s", strerror(errno));
ExitNow();
}
@@ -554,7 +579,7 @@
struct nlmsgerr *errMsg = reinterpret_cast<struct nlmsgerr *>(NLMSG_DATA(header));
OT_UNUSED_VARIABLE(errMsg);
- otLogWarnPlat("netlink NLMSG_ERROR response: seq=%u, error=%d", header->nlmsg_seq, errMsg->error);
+ LogWarn("netlink NLMSG_ERROR response: seq=%u, error=%d", header->nlmsg_seq, errMsg->error);
break;
}
default:
@@ -566,6 +591,8 @@
return;
}
+#endif // #ifdef __linux__
+
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
void InfraNetif::ReceiveIcmp6Message(void)
{
@@ -599,7 +626,7 @@
rval = recvmsg(mInfraIfIcmp6Socket, &msg, 0);
if (rval < 0)
{
- otLogWarnPlat("Failed to receive ICMPv6 message: %s", strerror(errno));
+ LogWarn("Failed to receive ICMPv6 message: %s", strerror(errno));
ExitNow(error = OT_ERROR_DROP);
}
@@ -635,7 +662,7 @@
exit:
if (error != OT_ERROR_NONE)
{
- otLogDebgPlat("Failed to handle ICMPv6 message: %s", otThreadErrorToString(error));
+ LogDebg("Failed to handle ICMPv6 message: %s", otThreadErrorToString(error));
}
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
@@ -655,7 +682,7 @@
VerifyOrExit((char *)req->ar_name == kWellKnownIpv4OnlyName);
- otLogInfoPlat("Handling host address response for %s", kWellKnownIpv4OnlyName);
+ LogInfo("Handling host address response for %s", kWellKnownIpv4OnlyName);
// We extract the first valid NAT64 prefix from the address look-up response.
for (struct addrinfo *rp = res; rp != NULL && prefix.mLength == 0; rp = rp->ai_next)
@@ -754,10 +781,10 @@
if (status != 0)
{
- otLogNotePlat("getaddrinfo_a failed: %s", gai_strerror(status));
+ LogNote("getaddrinfo_a failed: %s", gai_strerror(status));
ExitNow(error = OT_ERROR_FAILED);
}
- otLogInfoPlat("getaddrinfo_a requested for %s", kWellKnownIpv4OnlyName);
+ LogInfo("getaddrinfo_a requested for %s", kWellKnownIpv4OnlyName);
exit:
if (error != OT_ERROR_NONE)
{
@@ -792,7 +819,10 @@
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
VerifyOrExit(mInfraIfIcmp6Socket != -1);
#endif
+
+#ifdef __linux__
VerifyOrExit(mNetLinkSocket != -1);
+#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
if (FD_ISSET(mInfraIfIcmp6Socket, &aContext.mReadFdSet))
@@ -801,10 +831,12 @@
}
#endif
+#ifdef __linux__
if (FD_ISSET(mNetLinkSocket, &aContext.mReadFdSet))
{
ReceiveNetLinkMessage();
}
+#endif
exit:
return;
diff --git a/src/posix/platform/infra_if.hpp b/src/posix/platform/infra_if.hpp
index e8aedd2..3eee248 100644
--- a/src/posix/platform/infra_if.hpp
+++ b/src/posix/platform/infra_if.hpp
@@ -31,15 +31,20 @@
* This file implements the infrastructure interface for posix.
*/
+#ifndef OT_POSIX_PLATFORM_INFRA_IF_HPP_
+#define OT_POSIX_PLATFORM_INFRA_IF_HPP_
+
#include "openthread-posix-config.h"
#include <net/if.h>
#include <openthread/nat64.h>
#include <openthread/openthread-system.h>
-#include "multicast_routing.hpp"
#include "core/common/non_copyable.hpp"
-#include "posix/platform/mainloop.hpp"
+
+#include "logger.hpp"
+#include "mainloop.hpp"
+#include "multicast_routing.hpp"
#if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE
@@ -50,9 +55,11 @@
* Manages infrastructure network interface.
*
*/
-class InfraNetif : public Mainloop::Source, private NonCopyable
+class InfraNetif : public Mainloop::Source, public Logger<InfraNetif>, private NonCopyable
{
public:
+ static const char kLogModuleName[]; ///< Module name used for logging.
+
/**
* Updates the fd_set and timeout for mainloop.
*
@@ -220,8 +227,12 @@
static const uint8_t kValidNat64PrefixLength[];
char mInfraIfName[IFNAMSIZ];
- uint32_t mInfraIfIndex = 0;
- int mNetLinkSocket = -1;
+ uint32_t mInfraIfIndex = 0;
+
+#ifdef __linux__
+ int mNetLinkSocket = -1;
+#endif
+
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
int mInfraIfIcmp6Socket = -1;
#endif
@@ -229,7 +240,10 @@
MulticastRoutingManager mMulticastRoutingManager;
#endif
- void ReceiveNetLinkMessage(void);
+#ifdef __linux__
+ void ReceiveNetLinkMessage(void);
+#endif
+
bool HasLinkLocalAddress(void) const;
static void DiscoverNat64PrefixDone(union sigval sv);
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
@@ -241,3 +255,5 @@
} // namespace Posix
} // namespace ot
#endif // OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE
+
+#endif // OT_POSIX_PLATFORM_INFRA_IF_HPP_
diff --git a/src/posix/platform/ip6_utils.hpp b/src/posix/platform/ip6_utils.hpp
index 39c7bc1..841c317 100644
--- a/src/posix/platform/ip6_utils.hpp
+++ b/src/posix/platform/ip6_utils.hpp
@@ -26,16 +26,83 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
+#ifndef OT_POSIX_PLATFORM_IP6_UTILS_HPP_
+#define OT_POSIX_PLATFORM_IP6_UTILS_HPP_
+
#include "openthread-posix-config.h"
#include "platform-posix.h"
#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openthread/ip6.h>
namespace ot {
namespace Posix {
namespace Ip6Utils {
/**
+ * Indicates whether or not the IPv6 address scope is Link-Local.
+ *
+ * @param[in] aAddress The IPv6 address to check.
+ *
+ * @retval TRUE If the IPv6 address scope is Link-Local.
+ * @retval FALSE If the IPv6 address scope is not Link-Local.
+ *
+ */
+inline bool IsIp6AddressLinkLocal(const otIp6Address &aAddress)
+{
+ return (aAddress.mFields.m8[0] == 0xfe) && ((aAddress.mFields.m8[1] & 0xc0) == 0x80);
+}
+
+/**
+ * Indicates whether or not the IPv6 address is multicast.
+ *
+ * @param[in] aAddress The IPv6 address to check.
+ *
+ * @retval TRUE If the IPv6 address scope is multicast.
+ * @retval FALSE If the IPv6 address scope is not multicast.
+ *
+ */
+inline bool IsIp6AddressMulticast(const otIp6Address &aAddress) { return (aAddress.mFields.m8[0] == 0xff); }
+
+/**
+ * Indicates whether or not the IPv6 address is unspecified.
+ *
+ * @param[in] aAddress The IPv6 address to check.
+ *
+ * @retval TRUE If the IPv6 address scope is unspecified.
+ * @retval FALSE If the IPv6 address scope is not unspecified.
+ *
+ */
+inline bool IsIp6AddressUnspecified(const otIp6Address &aAddress) { return otIp6IsAddressUnspecified(&aAddress); }
+
+/**
+ * Copies the IPv6 address bytes into a given buffer.
+ *
+ * @param[in] aAddress The IPv6 address to copy.
+ * @param[in] aBuffer A pointer to buffer to copy the address to.
+ *
+ */
+inline void CopyIp6AddressTo(const otIp6Address &aAddress, void *aBuffer)
+{
+ memcpy(aBuffer, &aAddress, sizeof(otIp6Address));
+}
+
+/**
+ * Reads and set the the IPv6 address bytes from a given buffer.
+ *
+ * @param[in] aBuffer A pointer to buffer to read from.
+ * @param[out] aAddress A reference to populate with the read IPv6 address.
+ *
+ */
+inline void ReadIp6AddressFrom(const void *aBuffer, otIp6Address &aAddress)
+{
+ memcpy(&aAddress, aBuffer, sizeof(otIp6Address));
+}
+
+/**
* This utility class converts binary IPv6 address to text format.
*
*/
@@ -68,3 +135,5 @@
} // namespace Ip6Utils
} // namespace Posix
} // namespace ot
+
+#endif // OT_POSIX_PLATFORM_IP6_UTILS_HPP_
diff --git a/src/posix/platform/logger.hpp b/src/posix/platform/logger.hpp
new file mode 100644
index 0000000..e907b87
--- /dev/null
+++ b/src/posix/platform/logger.hpp
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file
+ * This file implements the `Logger` class for use by POSIX platform module.
+ */
+
+#ifndef OT_POSIX_PLATFORM_LOGGER_HPP_
+#define OT_POSIX_PLATFORM_LOGGER_HPP_
+
+#include "openthread-posix-config.h"
+
+#include <openthread/logging.h>
+
+namespace ot {
+namespace Posix {
+
+/**
+ * Provides logging methods for a specific POSIX module.
+ *
+ * The `Type` class MUST provide a `static const char kLogModuleName[]` which specifies the POSIX log module name to
+ * include in the platform logs (using `otLogPlatArgs()`).
+ *
+ * Users of this class should follow CRTP-style inheritance, i.e., the `Type` class itself should inherit from
+ * `Logger<Type>`.
+ *
+ */
+template <typename Type> class Logger
+{
+public:
+ /**
+ * Emits a log message at critical log level.
+ *
+ * @param[in] aFormat The format string.
+ * @param[in] ... Arguments for the format specification.
+ *
+ */
+ static void LogCrit(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2)
+ {
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_CRIT, Type::kLogModuleName, aFormat, args);
+ va_end(args);
+ }
+
+ /**
+ * Emits a log message at warning log level.
+ *
+ * @param[in] aFormat The format string.
+ * @param[in] ... Arguments for the format specification.
+ *
+ */
+ static void LogWarn(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2)
+ {
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_WARN, Type::kLogModuleName, aFormat, args);
+ va_end(args);
+ }
+
+ /**
+ * Emits a log message at note log level.
+ *
+ * @param[in] aFormat The format string.
+ * @param[in] ... Arguments for the format specification.
+ *
+ */
+ static void LogNote(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2)
+ {
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_NOTE, Type::kLogModuleName, aFormat, args);
+ va_end(args);
+ }
+
+ /**
+ * Emits a log message at info log level.
+ *
+ * @param[in] aFormat The format string.
+ * @param[in] ... Arguments for the format specification.
+ *
+ */
+ static void LogInfo(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2)
+ {
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_INFO, Type::kLogModuleName, aFormat, args);
+ va_end(args);
+ }
+
+ /**
+ * Emits a log message at debug log level.
+ *
+ * @param[in] aFormat The format string.
+ * @param[in] ... Arguments for the format specification.
+ *
+ */
+ static void LogDebg(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2)
+ {
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_DEBG, Type::kLogModuleName, aFormat, args);
+ va_end(args);
+ }
+};
+
+} // namespace Posix
+} // namespace ot
+
+#endif // OT_POSIX_PLATFORM_LOGGER_HPP_
diff --git a/src/posix/platform/mainloop.hpp b/src/posix/platform/mainloop.hpp
index 6e11e56..a9ce981 100644
--- a/src/posix/platform/mainloop.hpp
+++ b/src/posix/platform/mainloop.hpp
@@ -28,7 +28,7 @@
/**
* @file
- * This file includes definitions for the SPI interface to radio (RCP).
+ * This file includes definitions for the mainloop events and manager.
*/
#ifndef OT_POSIX_PLATFORM_MAINLOOP_HPP_
diff --git a/src/posix/platform/multicast_routing.cpp b/src/posix/platform/multicast_routing.cpp
index a224ae1..864f984 100644
--- a/src/posix/platform/multicast_routing.cpp
+++ b/src/posix/platform/multicast_routing.cpp
@@ -54,19 +54,21 @@
namespace ot {
namespace Posix {
-#define LogResult(aError, ...) \
- do \
- { \
- otError _err = (aError); \
- \
- if (_err == OT_ERROR_NONE) \
- { \
- otLogInfoPlat(OT_FIRST_ARG(__VA_ARGS__) ": %s" OT_REST_ARGS(__VA_ARGS__), otThreadErrorToString(_err)); \
- } \
- else \
- { \
- otLogWarnPlat(OT_FIRST_ARG(__VA_ARGS__) ": %s" OT_REST_ARGS(__VA_ARGS__), otThreadErrorToString(_err)); \
- } \
+const char MulticastRoutingManager::kLogModuleName[] = "McastRtMgr";
+
+#define LogResult(aError, ...) \
+ do \
+ { \
+ otError _err = (aError); \
+ \
+ if (_err == OT_ERROR_NONE) \
+ { \
+ LogInfo(OT_FIRST_ARG(__VA_ARGS__) ": %s" OT_REST_ARGS(__VA_ARGS__), otThreadErrorToString(_err)); \
+ } \
+ else \
+ { \
+ LogWarn(OT_FIRST_ARG(__VA_ARGS__) ": %s" OT_REST_ARGS(__VA_ARGS__), otThreadErrorToString(_err)); \
+ } \
} while (false)
void MulticastRoutingManager::SetUp(void)
@@ -114,7 +116,7 @@
InitMulticastRouterSock();
- LogResult(OT_ERROR_NONE, "MulticastRoutingManager: %s", __FUNCTION__);
+ LogResult(OT_ERROR_NONE, "%s", __FUNCTION__);
exit:
return;
}
@@ -123,7 +125,7 @@
{
FinalizeMulticastRouterSock();
- LogResult(OT_ERROR_NONE, "MulticastRoutingManager: %s", __FUNCTION__);
+ LogResult(OT_ERROR_NONE, "%s", __FUNCTION__);
}
void MulticastRoutingManager::Add(const Ip6::Address &aAddress)
@@ -133,7 +135,7 @@
UnblockInboundMulticastForwardingCache(aAddress);
UpdateMldReport(aAddress, true);
- LogResult(OT_ERROR_NONE, "MulticastRoutingManager: %s: %s", __FUNCTION__, aAddress.ToString().AsCString());
+ LogResult(OT_ERROR_NONE, "%s: %s", __FUNCTION__, aAddress.ToString().AsCString());
exit:
return;
@@ -148,7 +150,7 @@
RemoveInboundMulticastForwardingCache(aAddress);
UpdateMldReport(aAddress, false);
- LogResult(error, "MulticastRoutingManager: %s: %s", __FUNCTION__, aAddress.ToString().AsCString());
+ LogResult(error, "%s: %s", __FUNCTION__, aAddress.ToString().AsCString());
exit:
return;
@@ -166,8 +168,7 @@
? OT_ERROR_FAILED
: OT_ERROR_NONE);
- LogResult(error, "MulticastRoutingManager: %s: address %s %s", __FUNCTION__, aAddress.ToString().AsCString(),
- (isAdd ? "Added" : "Removed"));
+ LogResult(error, "%s: address %s %s", __FUNCTION__, aAddress.ToString().AsCString(), (isAdd ? "Added" : "Removed"));
}
bool MulticastRoutingManager::HasMulticastListener(const Ip6::Address &aAddress) const
@@ -283,7 +284,7 @@
error = AddMulticastForwardingCache(src, dst, static_cast<MifIndex>(mrt6msg->im6_mif));
exit:
- LogResult(error, "MulticastRoutingManager: %s", __FUNCTION__);
+ LogResult(error, "%s", __FUNCTION__);
}
otError MulticastRoutingManager::AddMulticastForwardingCache(const Ip6::Address &aSrcAddr,
@@ -340,9 +341,8 @@
SaveMulticastForwardingCache(aSrcAddr, aGroupAddr, aIif, forwardMif);
exit:
- LogResult(error, "MulticastRoutingManager: %s: add dynamic route: %s %s => %s %s", __FUNCTION__,
- MifIndexToString(aIif), aSrcAddr.ToString().AsCString(), aGroupAddr.ToString().AsCString(),
- MifIndexToString(forwardMif));
+ LogResult(error, "%s: add dynamic route: %s %s => %s %s", __FUNCTION__, MifIndexToString(aIif),
+ aSrcAddr.ToString().AsCString(), aGroupAddr.ToString().AsCString(), MifIndexToString(forwardMif));
return error;
}
@@ -377,7 +377,7 @@
mfc.Set(kMifIndexBackbone, kMifIndexThread);
- LogResult(error, "MulticastRoutingManager: %s: %s %s => %s %s", __FUNCTION__, MifIndexToString(mfc.mIif),
+ LogResult(error, "%s: %s %s => %s %s", __FUNCTION__, MifIndexToString(mfc.mIif),
mfc.mSrcAddr.ToString().AsCString(), mfc.mGroupAddr.ToString().AsCString(),
MifIndexToString(kMifIndexThread));
}
@@ -439,9 +439,9 @@
{
unsigned long validPktCnt;
- otLogDebgPlat("MulticastRoutingManager: %s: SIOCGETSGCNT_IN6 %s => %s: bytecnt=%lu, pktcnt=%lu, wrong_if=%lu",
- __FUNCTION__, aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(),
- sioc_sg_req6.bytecnt, sioc_sg_req6.pktcnt, sioc_sg_req6.wrong_if);
+ LogDebg("%s: SIOCGETSGCNT_IN6 %s => %s: bytecnt=%lu, pktcnt=%lu, wrong_if=%lu", __FUNCTION__,
+ aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(), sioc_sg_req6.bytecnt,
+ sioc_sg_req6.pktcnt, sioc_sg_req6.wrong_if);
validPktCnt = sioc_sg_req6.pktcnt - sioc_sg_req6.wrong_if;
if (validPktCnt != aMfc.mValidPktCnt)
@@ -453,8 +453,8 @@
}
else
{
- otLogDebgPlat("MulticastRoutingManager: %s: SIOCGETSGCNT_IN6 %s => %s failed: %s", __FUNCTION__,
- aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(), strerror(errno));
+ LogDebg("%s: SIOCGETSGCNT_IN6 %s => %s failed: %s", __FUNCTION__, aMfc.mSrcAddr.ToString().AsCString(),
+ aMfc.mGroupAddr.ToString().AsCString(), strerror(errno));
}
return updated;
@@ -483,19 +483,18 @@
void MulticastRoutingManager::DumpMulticastForwardingCache(void) const
{
#if OPENTHREAD_CONFIG_LOG_PLATFORM && (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG)
- otLogDebgPlat("MulticastRoutingManager: ==================== MFC ENTRIES ====================");
+ LogDebg("==================== MFC ENTRIES ====================");
for (const MulticastForwardingCache &mfc : mMulticastForwardingCacheTable)
{
if (mfc.IsValid())
{
- otLogDebgPlat("MulticastRoutingManager: %s %s => %s %s", MifIndexToString(mfc.mIif),
- mfc.mSrcAddr.ToString().AsCString(), mfc.mGroupAddr.ToString().AsCString(),
- MifIndexToString(mfc.mOif));
+ LogDebg("%s %s => %s %s", MifIndexToString(mfc.mIif), mfc.mSrcAddr.ToString().AsCString(),
+ mfc.mGroupAddr.ToString().AsCString(), MifIndexToString(mfc.mOif));
}
}
- otLogDebgPlat("MulticastRoutingManager: =====================================================");
+ LogDebg("=====================================================");
#endif
}
@@ -605,7 +604,7 @@
? OT_ERROR_NONE
: OT_ERROR_FAILED;
- LogResult(error, "MulticastRoutingManager: %s: %s %s => %s %s", __FUNCTION__, MifIndexToString(aMfc.mIif),
+ LogResult(error, "%s: %s %s => %s %s", __FUNCTION__, MifIndexToString(aMfc.mIif),
aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(),
MifIndexToString(aMfc.mOif));
diff --git a/src/posix/platform/multicast_routing.hpp b/src/posix/platform/multicast_routing.hpp
index c4d7e32..9d4e049 100644
--- a/src/posix/platform/multicast_routing.hpp
+++ b/src/posix/platform/multicast_routing.hpp
@@ -39,18 +39,21 @@
#include <openthread/backbone_router_ftd.h>
#include <openthread/openthread-system.h>
+#include "logger.hpp"
+#include "mainloop.hpp"
#include "platform-posix.h"
#include "core/common/non_copyable.hpp"
#include "core/net/ip6_address.hpp"
#include "lib/url/url.hpp"
-#include "posix/platform/mainloop.hpp"
namespace ot {
namespace Posix {
-class MulticastRoutingManager : public Mainloop::Source, private NonCopyable
+class MulticastRoutingManager : public Mainloop::Source, public Logger<MulticastRoutingManager>, private NonCopyable
{
public:
+ static const char kLogModuleName[];
+
explicit MulticastRoutingManager()
: mLastExpireTime(0)
diff --git a/src/posix/platform/netif.cpp b/src/posix/platform/netif.cpp
index d3b3024..e0a4c49 100644
--- a/src/posix/platform/netif.cpp
+++ b/src/posix/platform/netif.cpp
@@ -147,14 +147,14 @@
#include <openthread/message.h>
#include <openthread/nat64.h>
#include <openthread/netdata.h>
+#include <openthread/thread.h>
#include <openthread/platform/border_routing.h>
#include <openthread/platform/misc.h>
-#include "common/code_utils.hpp"
-#include "common/debug.hpp"
-#include "net/ip6_address.hpp"
-
+#include "ip6_utils.hpp"
+#include "logger.hpp"
#include "resolver.hpp"
+#include "common/code_utils.hpp"
unsigned int gNetifIndex = 0;
char gNetifName[IFNAMSIZ];
@@ -167,10 +167,10 @@
unsigned int otSysGetThreadNetifIndex(void) { return gNetifIndex; }
#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
+
#if OPENTHREAD_POSIX_CONFIG_FIREWALL_ENABLE
#include "firewall.hpp"
#endif
-#include "posix/platform/ip6_utils.hpp"
using namespace ot::Posix::Ip6Utils;
@@ -191,7 +191,7 @@
#define OPENTHREAD_POSIX_TUN_DEVICE "/dev/net/tun"
#endif
-#endif // OPENTHREAD_TUN_DEVICE
+#endif // OPENTHREAD_POSIX_TUN_DEVICE
#ifdef __linux__
static uint32_t sNetlinkSequence = 0; ///< Netlink message sequence.
@@ -291,33 +291,104 @@
#define OPENTHREAD_POSIX_LOG_TUN_PACKETS 0
+static const char kLogModuleName[] = "Netif";
+
+static void LogCrit(const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_CRIT, kLogModuleName, aFormat, args);
+ va_end(args);
+}
+
+static void LogWarn(const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_WARN, kLogModuleName, aFormat, args);
+ va_end(args);
+}
+
+static void LogNote(const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_NOTE, kLogModuleName, aFormat, args);
+ va_end(args);
+}
+
+static void LogInfo(const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_INFO, kLogModuleName, aFormat, args);
+ va_end(args);
+}
+
+static void LogDebg(const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_DEBG, kLogModuleName, aFormat, args);
+ va_end(args);
+}
+
#if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
-static const uint8_t allOnes[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+static const uint8_t kAllOnes[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
#define BITS_PER_BYTE 8
#define MAX_PREFIX_LENGTH (OT_IP6_ADDRESS_SIZE * BITS_PER_BYTE)
+static void CopyBits(uint8_t *aDst, const uint8_t *aSrc, uint8_t aNumBits)
+{
+ // Copies `aNumBits` from `aSrc` into `aDst` handling
+ // the case where `aNumBits` may not be a multiple of 8.
+ // Leaves the remaining bits beyond `aNumBits` in `aDst`
+ // unchanged.
+
+ uint8_t numBytes = aNumBits / BITS_PER_BYTE;
+ uint8_t extraBits = aNumBits % BITS_PER_BYTE;
+
+ memcpy(aDst, aSrc, numBytes);
+
+ if (extraBits > 0)
+ {
+ uint8_t mask = ((0x80 >> (extraBits - 1)) - 1);
+
+ aDst[numBytes] &= mask;
+ aDst[numBytes] |= (aSrc[numBytes] & ~mask);
+ }
+}
+
static void InitNetaskWithPrefixLength(struct in6_addr *address, uint8_t prefixLen)
{
- ot::Ip6::Address addr;
+ otIp6Address addr;
if (prefixLen > MAX_PREFIX_LENGTH)
{
prefixLen = MAX_PREFIX_LENGTH;
}
- addr.Clear();
- addr.SetPrefix(allOnes, prefixLen);
- memcpy(address, addr.mFields.m8, sizeof(addr.mFields.m8));
+ memset(&addr, 0, sizeof(otIp6Address));
+ CopyBits(addr.mFields.m8, kAllOnes, prefixLen);
+ CopyIp6AddressTo(addr, address);
}
static uint8_t NetmaskToPrefixLength(const struct sockaddr_in6 *netmask)
{
return otIp6PrefixMatch(reinterpret_cast<const otIp6Address *>(netmask->sin6_addr.s6_addr),
- reinterpret_cast<const otIp6Address *>(allOnes));
+ reinterpret_cast<const otIp6Address *>(kAllOnes));
}
-#endif
+
+#endif // defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
#ifdef __linux__
#pragma GCC diagnostic push
@@ -412,7 +483,9 @@
#endif
{
#if OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC > 0
- if (aAddressInfo.mScope > ot::Ip6::Address::kLinkLocalScope)
+ static constexpr kLinkLocalScope = 2;
+
+ if (aAddressInfo.mScope > kLinkLocalScope)
{
AddRtAttrUint32(&req.nh, sizeof(req), IFA_RT_PRIORITY, OPENTHREAD_POSIX_CONFIG_NETIF_PREFIX_ROUTE_METRIC);
}
@@ -421,13 +494,13 @@
if (send(sNetlinkFd, &req, req.nh.nlmsg_len, 0) != -1)
{
- otLogInfoPlat("[netif] Sent request#%u to %s %s/%u", sNetlinkSequence, (aIsAdded ? "add" : "remove"),
- Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength);
+ LogInfo("Sent request#%u to %s %s/%u", sNetlinkSequence, (aIsAdded ? "add" : "remove"),
+ Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength);
}
else
{
- otLogWarnPlat("[netif] Failed to send request#%u to %s %s/%u", sNetlinkSequence, (aIsAdded ? "add" : "remove"),
- Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength);
+ LogWarn("Failed to send request#%u to %s %s/%u", sNetlinkSequence, (aIsAdded ? "add" : "remove"),
+ Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength);
}
}
@@ -468,14 +541,13 @@
rval = ioctl(sIpFd, aIsAdded ? SIOCAIFADDR_IN6 : SIOCDIFADDR_IN6, &ifr6);
if (rval == 0)
{
- otLogInfoPlat("[netif] %s %s/%u", (aIsAdded ? "Added" : "Removed"),
- Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength);
+ LogInfo("%s %s/%u", (aIsAdded ? "Added" : "Removed"), Ip6AddressString(aAddressInfo.mAddress).AsCString(),
+ aAddressInfo.mPrefixLength);
}
else if (errno != EALREADY)
{
- otLogWarnPlat("[netif] Failed to %s %s/%u: %s", (aIsAdded ? "add" : "remove"),
- Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength,
- strerror(errno));
+ LogWarn("Failed to %s %s/%u: %s", (aIsAdded ? "add" : "remove"),
+ Ip6AddressString(aAddressInfo.mAddress).AsCString(), aAddressInfo.mPrefixLength, strerror(errno));
}
}
#endif
@@ -507,21 +579,20 @@
char addressString[INET6_ADDRSTRLEN + 1];
inet_ntop(AF_INET6, mreq.ipv6mr_multiaddr.s6_addr, addressString, sizeof(addressString));
- otLogWarnPlat("[netif] Ignoring %s failure (EINVAL) for MC LINKLOCAL address (%s)",
- aIsAdded ? "IPV6_JOIN_GROUP" : "IPV6_LEAVE_GROUP", addressString);
+ LogWarn("Ignoring %s failure (EINVAL) for MC LINKLOCAL address (%s)",
+ aIsAdded ? "IPV6_JOIN_GROUP" : "IPV6_LEAVE_GROUP", addressString);
err = 0;
}
#endif
if (err != 0)
{
- otLogWarnPlat("[netif] %s failure (%d)", aIsAdded ? "IPV6_JOIN_GROUP" : "IPV6_LEAVE_GROUP", errno);
+ LogWarn("%s failure (%d)", aIsAdded ? "IPV6_JOIN_GROUP" : "IPV6_LEAVE_GROUP", errno);
error = OT_ERROR_FAILED;
ExitNow();
}
- otLogInfoPlat("[netif] %s multicast address %s", aIsAdded ? "Added" : "Removed",
- Ip6AddressString(&aAddress).AsCString());
+ LogInfo("%s multicast address %s", aIsAdded ? "Added" : "Removed", Ip6AddressString(&aAddress).AsCString());
exit:
SuccessOrDie(error);
@@ -544,8 +615,8 @@
ifState = ((ifr.ifr_flags & IFF_UP) == IFF_UP) ? true : false;
- otLogNotePlat("[netif] Changing interface state to %s%s.", aState ? "up" : "down",
- (ifState == aState) ? " (already done, ignoring)" : "");
+ LogNote("Changing interface state to %s%s.", aState ? "up" : "down",
+ (ifState == aState) ? " (already done, ignoring)" : "");
if (ifState != aState)
{
@@ -560,7 +631,7 @@
exit:
if (error != OT_ERROR_NONE)
{
- otLogWarnPlat("[netif] Failed to update state %s", otThreadErrorToString(error));
+ LogWarn("Failed to update state %s", otThreadErrorToString(error));
}
}
@@ -723,15 +794,14 @@
otIp6PrefixToString(&sAddedOmrRoutes[i], prefixString, sizeof(prefixString));
if ((error = DeleteRoute(sAddedOmrRoutes[i])) != OT_ERROR_NONE)
{
- otLogWarnPlat("[netif] Failed to delete an OMR route %s in kernel: %s", prefixString,
- otThreadErrorToString(error));
+ LogWarn("Failed to delete an OMR route %s in kernel: %s", prefixString, otThreadErrorToString(error));
}
else
{
sAddedOmrRoutes[i] = sAddedOmrRoutes[sAddedOmrRoutesNum - 1];
--sAddedOmrRoutesNum;
--i;
- otLogInfoPlat("[netif] Successfully deleted an OMR route %s in kernel", prefixString);
+ LogInfo("Successfully deleted an OMR route %s in kernel", prefixString);
}
}
@@ -746,13 +816,12 @@
otIp6PrefixToString(&config.mPrefix, prefixString, sizeof(prefixString));
if ((error = AddOmrRoute(config.mPrefix)) != OT_ERROR_NONE)
{
- otLogWarnPlat("[netif] Failed to add an OMR route %s in kernel: %s", prefixString,
- otThreadErrorToString(error));
+ LogWarn("Failed to add an OMR route %s in kernel: %s", prefixString, otThreadErrorToString(error));
}
else
{
sAddedOmrRoutes[sAddedOmrRoutesNum++] = config.mPrefix;
- otLogInfoPlat("[netif] Successfully added an OMR route %s in kernel", prefixString);
+ LogInfo("Successfully added an OMR route %s in kernel", prefixString);
}
}
}
@@ -819,15 +888,14 @@
otIp6PrefixToString(&sAddedExternalRoutes[i], prefixString, sizeof(prefixString));
if ((error = DeleteRoute(sAddedExternalRoutes[i])) != OT_ERROR_NONE)
{
- otLogWarnPlat("[netif] Failed to delete an external route %s in kernel: %s", prefixString,
- otThreadErrorToString(error));
+ LogWarn("Failed to delete an external route %s in kernel: %s", prefixString, otThreadErrorToString(error));
}
else
{
sAddedExternalRoutes[i] = sAddedExternalRoutes[sAddedExternalRoutesNum - 1];
--sAddedExternalRoutesNum;
--i;
- otLogWarnPlat("[netif] Successfully deleted an external route %s in kernel", prefixString);
+ LogWarn("Successfully deleted an external route %s in kernel", prefixString);
}
}
@@ -838,18 +906,17 @@
continue;
}
VerifyOrExit(sAddedExternalRoutesNum < kMaxExternalRoutesNum,
- otLogWarnPlat("[netif] No buffer to add more external routes in kernel"));
+ LogWarn("No buffer to add more external routes in kernel"));
otIp6PrefixToString(&config.mPrefix, prefixString, sizeof(prefixString));
if ((error = AddExternalRoute(config.mPrefix)) != OT_ERROR_NONE)
{
- otLogWarnPlat("[netif] Failed to add an external route %s in kernel: %s", prefixString,
- otThreadErrorToString(error));
+ LogWarn("Failed to add an external route %s in kernel: %s", prefixString, otThreadErrorToString(error));
}
else
{
sAddedExternalRoutes[sAddedExternalRoutesNum++] = config.mPrefix;
- otLogWarnPlat("[netif] Successfully added an external route %s in kernel", prefixString);
+ LogWarn("Successfully added an external route %s in kernel", prefixString);
}
}
exit:
@@ -914,30 +981,30 @@
{
if ((error = DeleteIp4Route(sActiveNat64Cidr)) != OT_ERROR_NONE)
{
- otLogWarnPlat("[netif] failed to delete route for NAT64: %s", otThreadErrorToString(error));
+ LogWarn("failed to delete route for NAT64: %s", otThreadErrorToString(error));
}
}
sActiveNat64Cidr = translatorCidr;
otIp4CidrToString(&translatorCidr, cidrString, sizeof(cidrString));
- otLogInfoPlat("[netif] NAT64 CIDR updated to %s.", cidrString);
+ LogInfo("NAT64 CIDR updated to %s.", cidrString);
}
if (otNat64GetTranslatorState(gInstance) == OT_NAT64_STATE_ACTIVE)
{
if ((error = AddIp4Route(sActiveNat64Cidr, kNat64RoutePriority)) != OT_ERROR_NONE)
{
- otLogWarnPlat("[netif] failed to add route for NAT64: %s", otThreadErrorToString(error));
+ LogWarn("failed to add route for NAT64: %s", otThreadErrorToString(error));
}
- otLogInfoPlat("[netif] Adding route for NAT64");
+ LogInfo("Adding route for NAT64");
}
else if (sActiveNat64Cidr.mLength > 0) // Translator is not active.
{
if ((error = DeleteIp4Route(sActiveNat64Cidr)) != OT_ERROR_NONE)
{
- otLogWarnPlat("[netif] failed to delete route for NAT64: %s", otThreadErrorToString(error));
+ LogWarn("failed to delete route for NAT64: %s", otThreadErrorToString(error));
}
- otLogInfoPlat("[netif] Deleting route for NAT64");
+ LogInfo("Deleting route for NAT64");
}
exit:
@@ -993,7 +1060,7 @@
VerifyOrExit(otMessageRead(aMessage, 0, &packet[offset], maxLength) == length, error = OT_ERROR_NO_BUFS);
#if OPENTHREAD_POSIX_LOG_TUN_PACKETS
- otLogInfoPlat("[netif] Packet from NCP (%u bytes)", static_cast<uint16_t>(length));
+ LogInfo("Packet from NCP (%u bytes)", static_cast<uint16_t>(length));
otDumpInfoPlat("", &packet[offset], length);
#endif
@@ -1012,7 +1079,7 @@
if (error != OT_ERROR_NONE)
{
- otLogWarnPlat("[netif] Failed to receive, error:%s", otThreadErrorToString(error));
+ LogWarn("Failed to receive, error:%s", otThreadErrorToString(error));
}
}
@@ -1069,7 +1136,7 @@
VerifyOrExit(ra != nullptr, error = OT_ERROR_INVALID_ARGS);
#if OPENTHREAD_POSIX_LOG_TUN_PACKETS
- otLogInfoPlat("[netif] RA to BorderRouting (%hu bytes)", static_cast<uint16_t>(length));
+ LogInfo("RA to BorderRouting (%hu bytes)", static_cast<uint16_t>(length));
otDumpInfoPlat("", data, static_cast<size_t>(length));
#endif
@@ -1169,7 +1236,7 @@
}
#if OPENTHREAD_POSIX_LOG_TUN_PACKETS
- otLogInfoPlat("[netif] Packet to NCP (%hu bytes)", static_cast<uint16_t>(rval));
+ LogInfo("Packet to NCP (%hu bytes)", static_cast<uint16_t>(rval));
otDumpInfoPlat("", &packet[offset], static_cast<size_t>(rval));
#endif
@@ -1192,33 +1259,33 @@
{
if (error == OT_ERROR_DROP)
{
- otLogInfoPlat("[netif] Message dropped by Thread");
+ LogInfo("Message dropped by Thread");
}
else
{
- otLogWarnPlat("[netif] Failed to transmit, error:%s", otThreadErrorToString(error));
+ LogWarn("Failed to transmit, error:%s", otThreadErrorToString(error));
}
}
}
-static void logAddrEvent(bool isAdd, const ot::Ip6::Address &aAddress, otError error)
+static void logAddrEvent(bool isAdd, const otIp6Address &aAddress, otError error)
{
OT_UNUSED_VARIABLE(aAddress);
if ((error == OT_ERROR_NONE) || ((isAdd) && (error == OT_ERROR_ALREADY || error == OT_ERROR_REJECTED)) ||
((!isAdd) && (error == OT_ERROR_NOT_FOUND || error == OT_ERROR_REJECTED)))
{
- otLogInfoPlat("[netif] %s [%s] %s%s", isAdd ? "ADD" : "DEL", aAddress.IsMulticast() ? "M" : "U",
- aAddress.ToString().AsCString(),
- error == OT_ERROR_ALREADY ? " (already subscribed, ignored)"
- : error == OT_ERROR_REJECTED ? " (rejected)"
- : error == OT_ERROR_NOT_FOUND ? " (not found, ignored)"
- : "");
+ LogInfo("%s [%s] %s%s", isAdd ? "ADD" : "DEL", IsIp6AddressMulticast(aAddress) ? "M" : "U",
+ Ip6AddressString(&aAddress).AsCString(),
+ error == OT_ERROR_ALREADY ? " (already subscribed, ignored)"
+ : error == OT_ERROR_REJECTED ? " (rejected)"
+ : error == OT_ERROR_NOT_FOUND ? " (not found, ignored)"
+ : "");
}
else
{
- otLogWarnPlat("[netif] %s [%s] %s failed (%s)", isAdd ? "ADD" : "DEL", aAddress.IsMulticast() ? "M" : "U",
- aAddress.ToString().AsCString(), otThreadErrorToString(error));
+ LogWarn("%s [%s] %s failed (%s)", isAdd ? "ADD" : "DEL", IsIp6AddressMulticast(aAddress) ? "M" : "U",
+ Ip6AddressString(&aAddress).AsCString(), otThreadErrorToString(error));
}
}
@@ -1246,8 +1313,9 @@
case IFA_ANYCAST:
case IFA_MULTICAST:
{
- ot::Ip6::Address addr;
- memcpy(&addr, RTA_DATA(rta), sizeof(addr));
+ otIp6Address addr;
+
+ ReadIp6AddressFrom(RTA_DATA(rta), addr);
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_family = AF_INET6;
@@ -1257,14 +1325,14 @@
// which blocks openthread deriving an address by SLAAC and will cause routing issues.
// Ignore the required anycast addresses here to allow OpenThread stack generate one when necessary,
// and Linux will prefer the non-required anycast address on the interface.
- if (isRequiredAnycast(addr.GetBytes(), ifaddr->ifa_prefixlen))
+ if (isRequiredAnycast(addr.mFields.m8, ifaddr->ifa_prefixlen))
{
continue;
}
if (aNetlinkMessage->nlmsg_type == RTM_NEWADDR)
{
- if (!addr.IsMulticast())
+ if (!IsIp6AddressMulticast(addr))
{
otNetifAddress netAddr;
@@ -1283,6 +1351,7 @@
}
logAddrEvent(/* isAdd */ true, addr, error);
+
if (error == OT_ERROR_ALREADY || error == OT_ERROR_REJECTED)
{
error = OT_ERROR_NONE;
@@ -1292,7 +1361,7 @@
}
else if (aNetlinkMessage->nlmsg_type == RTM_DELADDR)
{
- if (!addr.IsMulticast())
+ if (!IsIp6AddressMulticast(addr))
{
error = otIp6RemoveUnicastAddress(aInstance, &addr);
}
@@ -1302,6 +1371,7 @@
}
logAddrEvent(/* isAdd */ false, addr, error);
+
if (error == OT_ERROR_NOT_FOUND || error == OT_ERROR_REJECTED)
{
error = OT_ERROR_NONE;
@@ -1317,7 +1387,7 @@
}
default:
- otLogDebgPlat("[netif] Unexpected address type (%d).", (int)rta->rta_type);
+ LogDebg("Unexpected address type (%d).", (int)rta->rta_type);
break;
}
}
@@ -1325,7 +1395,7 @@
exit:
if (error != OT_ERROR_NONE)
{
- otLogWarnPlat("[netif] Failed to process event, error:%s", otThreadErrorToString(error));
+ LogWarn("Failed to process event, error:%s", otThreadErrorToString(error));
}
}
@@ -1339,13 +1409,13 @@
isUp = ((ifinfo->ifi_flags & IFF_UP) != 0);
- otLogInfoPlat("[netif] Host netif is %s", isUp ? "up" : "down");
+ LogInfo("Host netif is %s", isUp ? "up" : "down");
#if defined(RTM_NEWLINK) && defined(RTM_DELLINK)
if (sIsSyncingState)
{
VerifyOrExit(isUp == otIp6IsEnabled(aInstance),
- otLogWarnPlat("[netif] Host netif state notification is unexpected (ignore)"));
+ LogWarn("Host netif state notification is unexpected (ignore)"));
sIsSyncingState = false;
}
else
@@ -1353,7 +1423,7 @@
if (isUp != otIp6IsEnabled(aInstance))
{
SuccessOrExit(error = otIp6SetEnabled(aInstance, isUp));
- otLogInfoPlat("[netif] Succeeded to sync netif state with host");
+ LogInfo("Succeeded to sync netif state with host");
}
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE
@@ -1362,7 +1432,7 @@
// Recover NAT64 route.
if ((error = AddIp4Route(sActiveNat64Cidr, kNat64RoutePriority)) != OT_ERROR_NONE)
{
- otLogWarnPlat("[netif] failed to add route for NAT64: %s", otThreadErrorToString(error));
+ LogWarn("failed to add route for NAT64: %s", otThreadErrorToString(error));
}
}
#endif
@@ -1370,7 +1440,7 @@
exit:
if (error != OT_ERROR_NONE)
{
- otLogWarnPlat("[netif] Failed to sync netif state with host: %s", otThreadErrorToString(error));
+ LogWarn("Failed to sync netif state with host: %s", otThreadErrorToString(error));
}
}
#endif // __linux__
@@ -1455,7 +1525,10 @@
if (addr6.sin6_family == AF_INET6)
{
+ otIp6Address addr;
+
is_link_local = false;
+
if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr))
{
is_link_local = true;
@@ -1467,8 +1540,7 @@
addr6.sin6_addr.s6_addr[3] = 0;
}
- ot::Ip6::Address addr;
- memcpy(&addr, &addr6.sin6_addr, sizeof(addr));
+ ReadIp6AddressFrom(&addr6.sin6_addr, addr);
if (rtm->rtm_type == RTM_NEWADDR
#ifdef RTM_NEWMADDR
@@ -1476,7 +1548,7 @@
#endif
)
{
- if (!addr.IsMulticast())
+ if (!IsIp6AddressMulticast(addr))
{
otNetifAddress netAddr;
@@ -1519,16 +1591,14 @@
err = ioctl(sIpFd, SIOCDIFADDR_IN6, &ifr6);
if (err != 0)
{
- otLogWarnPlat(
- "[netif] Error (%d) removing stack-addded link-local address %s", errno,
- inet_ntop(AF_INET6, addr6.sin6_addr.s6_addr, addressString, sizeof(addressString)));
+ LogWarn("Error (%d) removing stack-addded link-local address %s", errno,
+ inet_ntop(AF_INET6, addr6.sin6_addr.s6_addr, addressString, sizeof(addressString)));
error = OT_ERROR_FAILED;
}
else
{
- otLogNotePlat(
- "[netif] %s (removed stack-added link-local)",
- inet_ntop(AF_INET6, addr6.sin6_addr.s6_addr, addressString, sizeof(addressString)));
+ LogNote(" %s (removed stack-added link-local)",
+ inet_ntop(AF_INET6, addr6.sin6_addr.s6_addr, addressString, sizeof(addressString)));
error = OT_ERROR_NONE;
}
}
@@ -1536,6 +1606,7 @@
{
error = otIp6AddUnicastAddress(aInstance, &netAddr);
logAddrEvent(/* isAdd */ true, addr, error);
+
if (error == OT_ERROR_ALREADY)
{
error = OT_ERROR_NONE;
@@ -1551,6 +1622,7 @@
error = otIp6SubscribeMulticastAddress(aInstance, &addr);
logAddrEvent(/* isAdd */ true, addr, error);
+
if (error == OT_ERROR_ALREADY || error == OT_ERROR_REJECTED)
{
error = OT_ERROR_NONE;
@@ -1564,10 +1636,11 @@
#endif
)
{
- if (!addr.IsMulticast())
+ if (!IsIp6AddressMulticast(addr))
{
error = otIp6RemoveUnicastAddress(aInstance, &addr);
logAddrEvent(/* isAdd */ false, addr, error);
+
if (error == OT_ERROR_NOT_FOUND)
{
error = OT_ERROR_NONE;
@@ -1577,11 +1650,13 @@
{
error = otIp6UnsubscribeMulticastAddress(aInstance, &addr);
logAddrEvent(/* isAdd */ false, addr, error);
+
if (error == OT_ERROR_NOT_FOUND)
{
error = OT_ERROR_NONE;
}
}
+
SuccessOrExit(error);
}
}
@@ -1601,7 +1676,7 @@
exit:
if (error != OT_ERROR_NONE)
{
- otLogWarnPlat("[netif] Failed to process info event: %s", otThreadErrorToString(error));
+ LogWarn("Failed to process info event: %s", otThreadErrorToString(error));
}
}
@@ -1636,7 +1711,7 @@
if (msg->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
{
- otLogWarnPlat("[netif] Truncated netlink reply of request#%u", requestSeq);
+ LogWarn("Truncated netlink reply of request#%u", requestSeq);
ExitNow();
}
@@ -1645,7 +1720,7 @@
if (err->error == 0)
{
- otLogInfoPlat("[netif] Succeeded to process request#%u", requestSeq);
+ LogInfo("Succeeded to process request#%u", requestSeq);
ExitNow();
}
@@ -1671,11 +1746,11 @@
}
else
{
- otLogDebgPlat("[netif] Ignoring netlink response attribute %d (request#%u)", rta->rta_type, requestSeq);
+ LogDebg("Ignoring netlink response attribute %d (request#%u)", rta->rta_type, requestSeq);
}
}
- otLogWarnPlat("[netif] Failed to process request#%u: %s", requestSeq, errorMsg);
+ LogWarn("Failed to process request#%u: %s", requestSeq, errorMsg);
exit:
return;
@@ -1709,7 +1784,7 @@
// Ensures full netlink header is received
if (length < static_cast<ssize_t>(HEADER_SIZE))
{
- otLogWarnPlat("[netif] Unexpected netlink recv() result: %ld", static_cast<long>(length));
+ LogWarn("Unexpected netlink recv() result: %ld", static_cast<long>(length));
ExitNow();
}
@@ -1767,7 +1842,7 @@
#if defined(ROUTE_FILTER) || defined(RO_MSGFILTER) || defined(__linux__)
default:
- otLogWarnPlat("[netif] Unhandled/Unexpected netlink/route message (%d).", (int)msg->nlmsg_type);
+ LogWarn("Unhandled/Unexpected netlink/route message (%d).", (int)msg->nlmsg_type);
break;
#else
// this platform doesn't support filtering, so we expect messages of other types...we just ignore them
@@ -1848,11 +1923,13 @@
{
MLDv2Record *record = reinterpret_cast<MLDv2Record *>(&buffer[offset]);
- otError err;
- ot::Ip6::Address address;
+ otError err;
+ otIp6Address address;
- memcpy(&address.mFields.m8, &record->mMulticastAddress, sizeof(address.mFields.m8));
+ ReadIp6AddressFrom(&record->mMulticastAddress, address);
+
inet_ntop(AF_INET6, &record->mMulticastAddress, addressString, sizeof(addressString));
+
if (record->mRecordType == kICMPv6MLDv2RecordChangeToIncludeType)
{
err = otIp6SubscribeMulticastAddress(aInstance, &address);
@@ -1912,11 +1989,11 @@
if (send(sNetlinkFd, &req, req.nh.nlmsg_len, 0) != -1)
{
- otLogInfoPlat("[netif] Sent request#%u to set addr_gen_mode to %d", sNetlinkSequence, mode);
+ LogInfo("Sent request#%u to set addr_gen_mode to %d", sNetlinkSequence, mode);
}
else
{
- otLogWarnPlat("[netif] Failed to send request#%u to set addr_gen_mode to %d", sNetlinkSequence, mode);
+ LogWarn("Failed to send request#%u to set addr_gen_mode to %d", sNetlinkSequence, mode);
}
}
@@ -1997,7 +2074,7 @@
err = getsockopt(sTunFd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, gNetifName, &devNameLen);
VerifyOrDie(err == 0, OT_EXIT_ERROR_ERRNO);
- otLogInfoPlat("[netif] Tunnel device name = '%s'", gNetifName);
+ LogInfo("Tunnel device name = '%s'", gNetifName);
}
#endif
@@ -2069,13 +2146,13 @@
#if defined(NETLINK_EXT_ACK)
if (setsockopt(sNetlinkFd, SOL_NETLINK, NETLINK_EXT_ACK, &enable, sizeof(enable)) != 0)
{
- otLogWarnPlat("[netif] Failed to enable NETLINK_EXT_ACK: %s", strerror(errno));
+ LogWarn("Failed to enable NETLINK_EXT_ACK: %s", strerror(errno));
}
#endif
#if defined(NETLINK_CAP_ACK)
if (setsockopt(sNetlinkFd, SOL_NETLINK, NETLINK_CAP_ACK, &enable, sizeof(enable)) != 0)
{
- otLogWarnPlat("[netif] Failed to enable NETLINK_CAP_ACK: %s", strerror(errno));
+ LogWarn("Failed to enable NETLINK_CAP_ACK: %s", strerror(errno));
}
#endif
}
@@ -2120,6 +2197,13 @@
void platformNetifInit(otPlatformConfig *aPlatformConfig)
{
+ // To silence "unused function" warning.
+ (void)LogCrit;
+ (void)LogWarn;
+ (void)LogInfo;
+ (void)LogNote;
+ (void)LogDebg;
+
sIpFd = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_IP, kSocketNonBlock);
VerifyOrDie(sIpFd >= 0, OT_EXIT_ERROR_ERRNO);
@@ -2148,19 +2232,19 @@
{
if ((error = otNat64SetIp4Cidr(gInstance, &cidr)) != OT_ERROR_NONE)
{
- otLogWarnPlat("[netif] failed to set CIDR for NAT64: %s", otThreadErrorToString(error));
+ LogWarn("failed to set CIDR for NAT64: %s", otThreadErrorToString(error));
}
}
else
{
- otLogInfoPlat("[netif] No default NAT64 CIDR provided.");
+ LogInfo("No default NAT64 CIDR provided.");
}
}
#endif
void platformNetifSetUp(void)
{
- OT_ASSERT(gInstance != nullptr);
+ assert(gInstance != nullptr);
otIp6SetReceiveFilterEnabled(gInstance, true);
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
diff --git a/src/posix/platform/openthread-posix-config.h b/src/posix/platform/openthread-posix-config.h
index f39c6f2..ade8ab2 100644
--- a/src/posix/platform/openthread-posix-config.h
+++ b/src/posix/platform/openthread-posix-config.h
@@ -26,8 +26,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef OPENTHREAD_PLATFORM_CONFIG_H_
-#define OPENTHREAD_PLATFORM_CONFIG_H_
+#ifndef OPENTHREAD_PLATFORM_POSIX_CONFIG_H_
+#define OPENTHREAD_PLATFORM_POSIX_CONFIG_H_
#include "openthread-core-config.h"
@@ -429,4 +429,4 @@
#define OPENTHREAD_POSIX_CONFIG_TREL_TX_PACKET_POOL_SIZE 5
#endif
-#endif // OPENTHREAD_PLATFORM_CONFIG_H_
+#endif // OPENTHREAD_PLATFORM_POSIX_CONFIG_H_
diff --git a/src/posix/platform/platform-posix.h b/src/posix/platform/platform-posix.h
index 912ceb5..a5d3a30 100644
--- a/src/posix/platform/platform-posix.h
+++ b/src/posix/platform/platform-posix.h
@@ -32,8 +32,8 @@
* This file includes the platform-specific initializers.
*/
-#ifndef PLATFORM_POSIX_H_
-#define PLATFORM_POSIX_H_
+#ifndef OT_PLATFORM_POSIX_H_
+#define OT_PLATFORM_POSIX_H_
#include "openthread-posix-config.h"
@@ -424,4 +424,4 @@
#ifdef __cplusplus
}
#endif
-#endif // PLATFORM_POSIX_H_
+#endif // OT_PLATFORM_POSIX_H_
diff --git a/src/posix/platform/power.hpp b/src/posix/platform/power.hpp
index 4edbcb2..f4481f1 100644
--- a/src/posix/platform/power.hpp
+++ b/src/posix/platform/power.hpp
@@ -26,8 +26,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef POSIX_PLATFORM_POWER_H
-#define POSIX_PLATFORM_POWER_H
+#ifndef OT_POSIX_PLATFORM_POWER_HPP_
+#define OT_POSIX_PLATFORM_POWER_HPP_
#include <assert.h>
#include <stdio.h>
@@ -286,4 +286,4 @@
};
} // namespace Power
} // namespace ot
-#endif // POSIX_PLATFORM_POWER_H
+#endif // OT_POSIX_PLATFORM_POWER_HPP_
diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp
index 8828f9b..f5eb17c 100644
--- a/src/posix/platform/radio.cpp
+++ b/src/posix/platform/radio.cpp
@@ -57,6 +57,8 @@
extern "C" void platformRadioInit(const char *aUrl) { sRadio.Init(aUrl); }
} // namespace
+const char Radio::kLogModuleName[] = "Radio";
+
Radio::Radio(void)
: mRadioUrl(nullptr)
, mRadioSpinel()
@@ -98,7 +100,7 @@
mRadioSpinel.SetCallbacks(callbacks);
mRadioSpinel.Init(*mSpinelInterface, resetRadio, skipCompatibilityCheck, iidList, OT_ARRAY_LENGTH(iidList));
- otLogDebgPlat("instance init:%p - iid = %d", (void *)&mRadioSpinel, iidList[0]);
+ LogDebg("instance init:%p - iid = %d", (void *)&mRadioSpinel, iidList[0]);
ProcessRadioUrl(mRadioUrl);
}
@@ -145,7 +147,7 @@
#endif
else
{
- otLogCritPlat("The Spinel interface name \"%s\" is not supported!", aInterfaceName);
+ LogCrit("The Spinel interface name \"%s\" is not supported!", aInterfaceName);
DieNow(OT_ERROR_FAILED);
}
@@ -159,7 +161,7 @@
if (aRadioUrl.HasParam("ncp-dataset"))
{
- otLogCritPlat("The argument \"ncp-dataset\" is no longer supported");
+ LogCrit("The argument \"ncp-dataset\" is no longer supported");
DieNow(OT_ERROR_FAILED);
}
@@ -220,7 +222,7 @@
VerifyOrDie((error == OT_ERROR_NONE) || (error == OT_ERROR_NOT_IMPLEMENTED), OT_EXIT_FAILURE);
if (error == OT_ERROR_NOT_IMPLEMENTED)
{
- otLogWarnPlat("The RCP doesn't support setting the max transmit power");
+ LogWarn("The RCP doesn't support setting the max transmit power");
}
++channel;
@@ -233,7 +235,7 @@
VerifyOrDie((error == OT_ERROR_NONE) || (error == OT_ERROR_NOT_IMPLEMENTED), OT_ERROR_FAILED);
if (error == OT_ERROR_NOT_IMPLEMENTED)
{
- otLogWarnPlat("The RCP doesn't support setting the max transmit power");
+ LogWarn("The RCP doesn't support setting the max transmit power");
}
++channel;
diff --git a/src/posix/platform/radio.hpp b/src/posix/platform/radio.hpp
index 49cdfdf..e4a8b1a 100644
--- a/src/posix/platform/radio.hpp
+++ b/src/posix/platform/radio.hpp
@@ -26,15 +26,16 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef POSIX_PLATFORM_RADIO_HPP_
-#define POSIX_PLATFORM_RADIO_HPP_
+#ifndef OT_POSIX_PLATFORM_RADIO_HPP_
+#define OT_POSIX_PLATFORM_RADIO_HPP_
+#include "hdlc_interface.hpp"
+#include "logger.hpp"
+#include "radio_url.hpp"
+#include "spi_interface.hpp"
+#include "vendor_interface.hpp"
#include "common/code_utils.hpp"
#include "lib/spinel/radio_spinel.hpp"
-#include "posix/platform/hdlc_interface.hpp"
-#include "posix/platform/radio_url.hpp"
-#include "posix/platform/spi_interface.hpp"
-#include "posix/platform/vendor_interface.hpp"
#if OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_ENABLE
#ifdef OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER
#include OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER
@@ -48,9 +49,11 @@
* Manages Thread radio.
*
*/
-class Radio
+class Radio : public Logger<Radio>
{
public:
+ static const char kLogModuleName[]; ///< Module name used for logging.
+
/**
* Creates the radio manager.
*
@@ -123,4 +126,4 @@
} // namespace Posix
} // namespace ot
-#endif // POSIX_PLATFORM_RADIO_HPP_
+#endif // OT_POSIX_PLATFORM_RADIO_HPP_
diff --git a/src/posix/platform/radio_url.hpp b/src/posix/platform/radio_url.hpp
index 0246070..0e088cb 100644
--- a/src/posix/platform/radio_url.hpp
+++ b/src/posix/platform/radio_url.hpp
@@ -26,8 +26,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef POSIX_PLATFORM_RADIO_URL_HPP_
-#define POSIX_PLATFORM_RADIO_URL_HPP_
+#ifndef OT_POSIX_PLATFORM_RADIO_URL_HPP_
+#define OT_POSIX_PLATFORM_RADIO_URL_HPP_
#include <stdio.h>
#include <stdlib.h>
@@ -78,4 +78,4 @@
} // namespace Posix
} // namespace ot
-#endif // POSIX_PLATFORM_RADIO_URL_HPP_
+#endif // OT_POSIX_PLATFORM_RADIO_URL_HPP_
diff --git a/src/posix/platform/resolver.cpp b/src/posix/platform/resolver.cpp
index c8d80e2..642d771 100644
--- a/src/posix/platform/resolver.cpp
+++ b/src/posix/platform/resolver.cpp
@@ -61,6 +61,8 @@
namespace ot {
namespace Posix {
+const char Resolver::kLogModuleName[] = "Resolver";
+
void Resolver::Init(void)
{
memset(mUpstreamTransaction, 0, sizeof(mUpstreamTransaction));
@@ -95,8 +97,7 @@
if (inet_pton(AF_INET, &line.c_str()[sizeof(kNameserverItem)], &addr) == 1)
{
- otLogInfoPlat("Got nameserver #%d: %s", mUpstreamDnsServerCount,
- &line.c_str()[sizeof(kNameserverItem)]);
+ LogInfo("Got nameserver #%d: %s", mUpstreamDnsServerCount, &line.c_str()[sizeof(kNameserverItem)]);
mUpstreamDnsServerList[mUpstreamDnsServerCount] = addr;
mUpstreamDnsServerCount++;
}
@@ -105,7 +106,7 @@
if (mUpstreamDnsServerCount == 0)
{
- otLogCritPlat("No domain name servers found in %s, default to 127.0.0.1", kResolvConfFullPath);
+ LogCrit("No domain name servers found in %s, default to 127.0.0.1", kResolvConfFullPath);
}
mUpstreamDnsServerListFreshness = otPlatTimeGet();
@@ -137,12 +138,12 @@
sendto(txn->mUdpFd, packet, length, MSG_DONTWAIT, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) > 0,
error = OT_ERROR_NO_ROUTE);
}
- otLogInfoPlat("Forwarded DNS query %p to %d server(s).", static_cast<void *>(aTxn), mUpstreamDnsServerCount);
+ LogInfo("Forwarded DNS query %p to %d server(s).", static_cast<void *>(aTxn), mUpstreamDnsServerCount);
exit:
if (error != OT_ERROR_NONE)
{
- otLogCritPlat("Failed to forward DNS query %p to server: %d", static_cast<void *>(aTxn), error);
+ LogCrit("Failed to forward DNS query %p to server: %d", static_cast<void *>(aTxn), error);
}
return;
}
@@ -171,7 +172,7 @@
fdOrError = socket(AF_INET, SOCK_DGRAM, 0);
if (fdOrError < 0)
{
- otLogInfoPlat("Failed to create socket for upstream resolver: %d", fdOrError);
+ LogInfo("Failed to create socket for upstream resolver: %d", fdOrError);
break;
}
ret = &txn;
@@ -203,11 +204,11 @@
exit:
if (readSize < 0)
{
- otLogInfoPlat("Failed to read response from upstream resolver socket: %d", errno);
+ LogInfo("Failed to read response from upstream resolver socket: %d", errno);
}
if (error != OT_ERROR_NONE)
{
- otLogInfoPlat("Failed to forward upstream DNS response: %s", otThreadErrorToString(error));
+ LogInfo("Failed to forward upstream DNS response: %s", otThreadErrorToString(error));
}
if (message != nullptr)
{
diff --git a/src/posix/platform/resolver.hpp b/src/posix/platform/resolver.hpp
index 8446a47..fae0cf9 100644
--- a/src/posix/platform/resolver.hpp
+++ b/src/posix/platform/resolver.hpp
@@ -26,8 +26,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef POSIX_PLATFORM_RESOLVER_HPP_
-#define POSIX_PLATFORM_RESOLVER_HPP_
+#ifndef OT_POSIX_PLATFORM_RESOLVER_HPP_
+#define OT_POSIX_PLATFORM_RESOLVER_HPP_
#include <openthread/openthread-system.h>
#include <openthread/platform/dns.h>
@@ -35,14 +35,18 @@
#include <arpa/inet.h>
#include <sys/select.h>
+#include "logger.hpp"
+
#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
namespace ot {
namespace Posix {
-class Resolver
+class Resolver : public Logger<Resolver>
{
public:
+ static const char kLogModuleName[]; ///< Module name used for logging.
+
constexpr static ssize_t kMaxDnsMessageSize = 512;
constexpr static ssize_t kMaxUpstreamTransactionCount = 16;
constexpr static ssize_t kMaxUpstreamServerCount = 3;
@@ -73,10 +77,7 @@
/**
* Updates the file descriptor sets with file descriptors used by the radio driver.
*
- * @param[in,out] aReadFdSet A reference to the read file descriptors.
- * @param[in,out] aErrorFdSet A reference to the error file descriptors.
- * @param[in,out] aMaxFd A reference to the max file descriptor.
- * @param[in,out] aTimeout A reference to the timeout.
+ * @param[in,out] aContext The mainloop context.
*
*/
void UpdateFdSet(otSysMainloopContext &aContext);
@@ -84,8 +85,7 @@
/**
* Handles the result of select.
*
- * @param[in] aReadFdSet A reference to the read file descriptors.
- * @param[in] aErrorFdSet A reference to the error file descriptors.
+ * @param[in] aContext The mainloop context.
*
*/
void Process(const otSysMainloopContext &aContext);
@@ -122,4 +122,4 @@
#endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE
-#endif // POSIX_PLATFORM_RESOLVER_HPP_
+#endif // OT_POSIX_PLATFORM_RESOLVER_HPP_
diff --git a/src/posix/platform/settings.hpp b/src/posix/platform/settings.hpp
index d2009aa..bf2cabd 100644
--- a/src/posix/platform/settings.hpp
+++ b/src/posix/platform/settings.hpp
@@ -26,8 +26,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef POSIX_PLATFORM_SETTINGS_HPP_
-#define POSIX_PLATFORM_SETTINGS_HPP_
+#ifndef OT_POSIX_PLATFORM_SETTINGS_HPP_
+#define OT_POSIX_PLATFORM_SETTINGS_HPP_
namespace ot {
namespace Posix {
@@ -100,4 +100,4 @@
} // namespace Posix
} // namespace ot
-#endif // POSIX_PLATFORM_SETTINGS_HPP_
+#endif // OT_POSIX_PLATFORM_SETTINGS_HPP_
diff --git a/src/posix/platform/spi_interface.cpp b/src/posix/platform/spi_interface.cpp
index 167f4b9..9d7860b 100644
--- a/src/posix/platform/spi_interface.cpp
+++ b/src/posix/platform/spi_interface.cpp
@@ -62,6 +62,8 @@
namespace ot {
namespace Posix {
+const char SpiInterface::kLogModuleName[] = "SpiIntface";
+
SpiInterface::SpiInterface(const Url::Url &aRadioUrl)
: mReceiveFrameCallback(nullptr)
, mReceiveFrameContext(nullptr)
@@ -153,7 +155,7 @@
}
else
{
- otLogNotePlat("SPI interface enters polling mode.");
+ LogNote("SPI interface enters polling mode.");
}
InitResetPin(spiGpioResetDevice, spiGpioResetLine);
@@ -254,7 +256,7 @@
char label[] = "SOC_THREAD_RESET";
int fd;
- otLogDebgPlat("InitResetPin: charDev=%s, line=%" PRIu8, aCharDev, aLine);
+ LogDebg("InitResetPin: charDev=%s, line=%" PRIu8, aCharDev, aLine);
VerifyOrDie(aCharDev != nullptr, OT_EXIT_INVALID_ARGUMENTS);
VerifyOrDie((fd = open(aCharDev, O_RDWR)) != -1, OT_EXIT_ERROR_ERRNO);
@@ -268,7 +270,7 @@
char label[] = "THREAD_SOC_INT";
int fd;
- otLogDebgPlat("InitIntPin: charDev=%s, line=%" PRIu8, aCharDev, aLine);
+ LogDebg("InitIntPin: charDev=%s, line=%" PRIu8, aCharDev, aLine);
VerifyOrDie(aCharDev != nullptr, OT_EXIT_INVALID_ARGUMENTS);
VerifyOrDie((fd = open(aCharDev, O_RDWR)) != -1, OT_EXIT_ERROR_ERRNO);
@@ -283,7 +285,7 @@
const uint8_t wordBits = kSpiBitsPerWord;
int fd;
- otLogDebgPlat("InitSpiDev: path=%s, mode=%" PRIu8 ", speed=%" PRIu32, aPath, aMode, aSpeed);
+ LogDebg("InitSpiDev: path=%s, mode=%" PRIu8 ", speed=%" PRIu32, aPath, aMode, aSpeed);
VerifyOrDie((aPath != nullptr) && (aMode <= kSpiModeMax), OT_EXIT_INVALID_ARGUMENTS);
VerifyOrDie((fd = open(aPath, O_RDWR | O_CLOEXEC)) != -1, OT_EXIT_ERROR_ERRNO);
@@ -314,7 +316,7 @@
// Set Reset pin to high level.
SetGpioValue(mResetGpioValueFd, 1);
- otLogNotePlat("Triggered hardware reset");
+ LogNote("Triggered hardware reset");
}
uint8_t *SpiInterface::GetRealRxFrameStart(uint8_t *aSpiRxFrameBuffer, uint8_t aAlignAllowance, uint16_t &aSkipLength)
@@ -453,12 +455,12 @@
if (error != OT_ERROR_NONE)
{
- otLogCritPlat("PushPullSpi:DoSpiTransfer: errno=%s", strerror(errno));
+ LogCrit("PushPullSpi:DoSpiTransfer: errno=%s", strerror(errno));
// Print out a helpful error message for a common error.
if ((mSpiCsDelayUs != 0) && (errno == EINVAL))
{
- otLogWarnPlat("SPI ioctl failed with EINVAL. Try adding `--spi-cs-delay=0` to command line arguments.");
+ LogWarn("SPI ioctl failed with EINVAL. Try adding `--spi-cs-delay=0` to command line arguments.");
}
LogStats();
@@ -471,10 +473,10 @@
{
Spinel::SpiFrame rxFrame(spiRxFrame);
- otLogDebgPlat("spi_transfer TX: H:%02X ACCEPT:%" PRIu16 " DATA:%" PRIu16, txFrame.GetHeaderFlagByte(),
- txFrame.GetHeaderAcceptLen(), txFrame.GetHeaderDataLen());
- otLogDebgPlat("spi_transfer RX: H:%02X ACCEPT:%" PRIu16 " DATA:%" PRIu16, rxFrame.GetHeaderFlagByte(),
- rxFrame.GetHeaderAcceptLen(), rxFrame.GetHeaderDataLen());
+ LogDebg("spi_transfer TX: H:%02X ACCEPT:%" PRIu16 " DATA:%" PRIu16, txFrame.GetHeaderFlagByte(),
+ txFrame.GetHeaderAcceptLen(), txFrame.GetHeaderDataLen());
+ LogDebg("spi_transfer RX: H:%02X ACCEPT:%" PRIu16 " DATA:%" PRIu16, rxFrame.GetHeaderFlagByte(),
+ rxFrame.GetHeaderAcceptLen(), rxFrame.GetHeaderDataLen());
slaveHeader = rxFrame.GetHeaderFlagByte();
if ((slaveHeader == 0xFF) || (slaveHeader == 0x00))
@@ -485,11 +487,11 @@
// Device is off or in a bad state. In some cases may be induced by flow control.
if (mSpiSlaveDataLen == 0)
{
- otLogDebgPlat("Slave did not respond to frame. (Header was all 0x%02X)", slaveHeader);
+ LogDebg("Slave did not respond to frame. (Header was all 0x%02X)", slaveHeader);
}
else
{
- otLogWarnPlat("Slave did not respond to frame. (Header was all 0x%02X)", slaveHeader);
+ LogWarn("Slave did not respond to frame. (Header was all 0x%02X)", slaveHeader);
}
mSpiUnresponsiveFrameCount++;
@@ -499,8 +501,8 @@
// Header is full of garbage
mInterfaceMetrics.mTransferredGarbageFrameCount++;
- otLogWarnPlat("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrame[0], spiRxFrame[1],
- spiRxFrame[2], spiRxFrame[3], spiRxFrame[4]);
+ LogWarn("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrame[0], spiRxFrame[1], spiRxFrame[2],
+ spiRxFrame[3], spiRxFrame[4]);
otDumpDebgPlat("SPI-TX", mSpiTxFrameBuffer, spiTransferBytes);
otDumpDebgPlat("SPI-RX", spiRxFrameBuffer, spiTransferBytes);
}
@@ -518,8 +520,8 @@
mSpiTxRefusedCount++;
mSpiSlaveDataLen = 0;
- otLogWarnPlat("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrame[0], spiRxFrame[1], spiRxFrame[2],
- spiRxFrame[3], spiRxFrame[4]);
+ LogWarn("Garbage in header : %02X %02X %02X %02X %02X", spiRxFrame[0], spiRxFrame[1], spiRxFrame[2],
+ spiRxFrame[3], spiRxFrame[4]);
otDumpDebgPlat("SPI-TX", mSpiTxFrameBuffer, spiTransferBytes);
otDumpDebgPlat("SPI-RX", spiRxFrameBuffer, spiTransferBytes);
@@ -532,7 +534,7 @@
{
mSlaveResetCount++;
- otLogNotePlat("Slave did reset (%" PRIu64 " resets so far)", mSlaveResetCount);
+ LogNote("Slave did reset (%" PRIu64 " resets so far)", mSlaveResetCount);
LogStats();
}
@@ -634,7 +636,7 @@
// Interrupt pin is asserted, set the timeout to be 0.
timeout.tv_sec = 0;
timeout.tv_usec = 0;
- otLogDebgPlat("UpdateFdSet(): Interrupt.");
+ LogDebg("UpdateFdSet(): Interrupt.");
}
else
{
@@ -677,7 +679,7 @@
{
// To avoid printing out this message over and over, we only print it out once the refused count is at two
// or higher when we actually have something to send the slave. And then, we only print it once.
- otLogInfoPlat("Slave is rate limiting transactions");
+ LogInfo("Slave is rate limiting transactions");
mDidPrintRateLimitLog = true;
}
@@ -686,7 +688,7 @@
{
// Ua-oh. The slave hasn't given us a chance to send it anything for over thirty frames. If this ever
// happens, print out a warning to the logs.
- otLogWarnPlat("Slave seems stuck.");
+ LogWarn("Slave seems stuck.");
}
else if (mSpiTxRefusedCount == kSpiTxRefuseExitCount)
{
@@ -716,7 +718,7 @@
{
struct gpioevent_data event;
- otLogDebgPlat("Process(): Interrupt.");
+ LogDebg("Process(): Interrupt.");
// Read event data to clear interrupt.
VerifyOrDie(read(mIntGpioValueFd, &event, sizeof(event)) != -1, OT_EXIT_ERROR_ERRNO);
@@ -804,21 +806,21 @@
void SpiInterface::LogError(const char *aString)
{
OT_UNUSED_VARIABLE(aString);
- otLogWarnPlat("%s: %s", aString, strerror(errno));
+ LogWarn("%s: %s", aString, strerror(errno));
}
void SpiInterface::LogStats(void)
{
- otLogInfoPlat("INFO: SlaveResetCount=%" PRIu64, mSlaveResetCount);
- otLogInfoPlat("INFO: SpiDuplexFrameCount=%" PRIu64, mSpiDuplexFrameCount);
- otLogInfoPlat("INFO: SpiUnresponsiveFrameCount=%" PRIu64, mSpiUnresponsiveFrameCount);
- otLogInfoPlat("INFO: TransferredFrameCount=%" PRIu64, mInterfaceMetrics.mTransferredFrameCount);
- otLogInfoPlat("INFO: TransferredValidFrameCount=%" PRIu64, mInterfaceMetrics.mTransferredValidFrameCount);
- otLogInfoPlat("INFO: TransferredGarbageFrameCount=%" PRIu64, mInterfaceMetrics.mTransferredGarbageFrameCount);
- otLogInfoPlat("INFO: RxFrameCount=%" PRIu64, mInterfaceMetrics.mRxFrameCount);
- otLogInfoPlat("INFO: RxFrameByteCount=%" PRIu64, mInterfaceMetrics.mRxFrameByteCount);
- otLogInfoPlat("INFO: TxFrameCount=%" PRIu64, mInterfaceMetrics.mTxFrameCount);
- otLogInfoPlat("INFO: TxFrameByteCount=%" PRIu64, mInterfaceMetrics.mTxFrameByteCount);
+ LogInfo("INFO: SlaveResetCount=%" PRIu64, mSlaveResetCount);
+ LogInfo("INFO: SpiDuplexFrameCount=%" PRIu64, mSpiDuplexFrameCount);
+ LogInfo("INFO: SpiUnresponsiveFrameCount=%" PRIu64, mSpiUnresponsiveFrameCount);
+ LogInfo("INFO: TransferredFrameCount=%" PRIu64, mInterfaceMetrics.mTransferredFrameCount);
+ LogInfo("INFO: TransferredValidFrameCount=%" PRIu64, mInterfaceMetrics.mTransferredValidFrameCount);
+ LogInfo("INFO: TransferredGarbageFrameCount=%" PRIu64, mInterfaceMetrics.mTransferredGarbageFrameCount);
+ LogInfo("INFO: RxFrameCount=%" PRIu64, mInterfaceMetrics.mRxFrameCount);
+ LogInfo("INFO: RxFrameByteCount=%" PRIu64, mInterfaceMetrics.mRxFrameByteCount);
+ LogInfo("INFO: TxFrameCount=%" PRIu64, mInterfaceMetrics.mTxFrameCount);
+ LogInfo("INFO: TxFrameByteCount=%" PRIu64, mInterfaceMetrics.mTxFrameByteCount);
}
} // namespace Posix
} // namespace ot
diff --git a/src/posix/platform/spi_interface.hpp b/src/posix/platform/spi_interface.hpp
index 095214a..94b5507 100644
--- a/src/posix/platform/spi_interface.hpp
+++ b/src/posix/platform/spi_interface.hpp
@@ -31,11 +31,12 @@
* This file includes definitions for the SPI interface to radio (RCP).
*/
-#ifndef POSIX_PLATFORM_SPI_INTERFACE_HPP_
-#define POSIX_PLATFORM_SPI_INTERFACE_HPP_
+#ifndef OT_POSIX_PLATFORM_SPI_INTERFACE_HPP_
+#define OT_POSIX_PLATFORM_SPI_INTERFACE_HPP_
#include "openthread-posix-config.h"
+#include "logger.hpp"
#include "platform-posix.h"
#include "lib/hdlc/hdlc.hpp"
#include "lib/spinel/multi_frame_buffer.hpp"
@@ -51,9 +52,11 @@
* Defines an SPI interface to the Radio Co-processor (RCP).
*
*/
-class SpiInterface : public ot::Spinel::SpinelInterface
+class SpiInterface : public ot::Spinel::SpinelInterface, public Logger<SpiInterface>
{
public:
+ static const char kLogModuleName[]; ///< Module name used for logging.
+
/**
* Initializes the object.
*
@@ -258,4 +261,4 @@
} // namespace Posix
} // namespace ot
-#endif // POSIX_PLATFORM_SPI_INTERFACE_HPP_
+#endif // OT_POSIX_PLATFORM_SPI_INTERFACE_HPP_
diff --git a/src/posix/platform/trel.cpp b/src/posix/platform/trel.cpp
index ea06bca..6b6e6c0 100644
--- a/src/posix/platform/trel.cpp
+++ b/src/posix/platform/trel.cpp
@@ -45,6 +45,7 @@
#include <openthread/logging.h>
#include <openthread/platform/trel.h>
+#include "logger.hpp"
#include "radio_url.hpp"
#include "system.hpp"
#include "common/code_utils.hpp"
@@ -73,6 +74,53 @@
static bool sEnabled = false;
static int sSocket = -1;
+static const char kLogModuleName[] = "Trel";
+
+static void LogCrit(const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_CRIT, kLogModuleName, aFormat, args);
+ va_end(args);
+}
+
+static void LogWarn(const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_WARN, kLogModuleName, aFormat, args);
+ va_end(args);
+}
+
+static void LogNote(const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_NOTE, kLogModuleName, aFormat, args);
+ va_end(args);
+}
+
+static void LogInfo(const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_INFO, kLogModuleName, aFormat, args);
+ va_end(args);
+}
+
+static void LogDebg(const char *aFormat, ...)
+{
+ va_list args;
+
+ va_start(args, aFormat);
+ otLogPlatArgs(OT_LOG_LEVEL_DEBG, kLogModuleName, aFormat, args);
+ va_end(args);
+}
+
static const char *Ip6AddrToString(const void *aAddress)
{
static char string[INET6_ADDRSTRLEN];
@@ -121,7 +169,7 @@
struct sockaddr_in6 sockAddr;
socklen_t sockLen;
- otLogDebgPlat("[trel] PrepareSocket()");
+ LogDebg("PrepareSocket()");
sSocket = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, 0, kSocketNonBlock);
VerifyOrDie(sSocket >= 0, OT_EXIT_ERROR_ERRNO);
@@ -141,7 +189,7 @@
if (bind(sSocket, (struct sockaddr *)&sockAddr, sizeof(sockAddr)) == -1)
{
- otLogCritPlat("[trel] Failed to bind socket");
+ LogCrit("Failed to bind socket");
DieNow(OT_EXIT_ERROR_ERRNO);
}
@@ -149,7 +197,7 @@
if (getsockname(sSocket, (struct sockaddr *)&sockAddr, &sockLen) == -1)
{
- otLogCritPlat("[trel] Failed to get the socket name");
+ LogCrit("Failed to get the socket name");
DieNow(OT_EXIT_ERROR_ERRNO);
}
@@ -173,7 +221,7 @@
if (ret != aLength)
{
- otLogDebgPlat("[trel] SendPacket() -- sendto() failed errno %d", errno);
+ LogDebg("SendPacket() -- sendto() failed errno %d", errno);
switch (errno)
{
@@ -194,8 +242,8 @@
}
exit:
- otLogDebgPlat("[trel] SendPacket([%s]:%u) err:%s pkt:%s", Ip6AddrToString(&aDestSockAddr->mAddress),
- aDestSockAddr->mPort, otThreadErrorToString(error), BufferToString(aBuffer, aLength));
+ LogDebg("SendPacket([%s]:%u) err:%s pkt:%s", Ip6AddrToString(&aDestSockAddr->mAddress), aDestSockAddr->mPort,
+ otThreadErrorToString(error), BufferToString(aBuffer, aLength));
if (error != OT_ERROR_NONE)
{
++sCounters.mTxFailure;
@@ -222,8 +270,8 @@
sRxPacketLength = sizeof(sRxPacketLength);
}
- otLogDebgPlat("[trel] ReceivePacket() - received from [%s]:%d, id:%d, pkt:%s", Ip6AddrToString(&sockAddr.sin6_addr),
- ntohs(sockAddr.sin6_port), sockAddr.sin6_scope_id, BufferToString(sRxPacketBuffer, sRxPacketLength));
+ LogDebg("ReceivePacket() - received from [%s]:%d, id:%d, pkt:%s", Ip6AddrToString(&sockAddr.sin6_addr),
+ ntohs(sockAddr.sin6_port), sockAddr.sin6_scope_id, BufferToString(sRxPacketBuffer, sRxPacketLength));
if (sEnabled)
{
@@ -257,7 +305,7 @@
if (SendPacket(packet->mBuffer, packet->mLength, &packet->mDestSockAddr) == OT_ERROR_INVALID_STATE)
{
- otLogDebgPlat("[trel] SendQueuedPackets() - SendPacket() would block");
+ LogDebg("SendQueuedPackets() - SendPacket() would block");
break;
}
@@ -287,7 +335,7 @@
// Allocate an available packet entry (from the free packet list)
// and copy the packet content into it.
- VerifyOrExit(sFreeTxPacketHead != NULL, otLogWarnPlat("[trel] EnqueuePacket failed, queue is full"));
+ VerifyOrExit(sFreeTxPacketHead != NULL, LogWarn("EnqueuePacket failed, queue is full"));
packet = sFreeTxPacketHead;
sFreeTxPacketHead = sFreeTxPacketHead->mNext;
@@ -309,8 +357,8 @@
sTxPacketQueueTail = packet;
}
- otLogDebgPlat("[trel] EnqueuePacket([%s]:%u) - %s", Ip6AddrToString(&aDestSockAddr->mAddress), aDestSockAddr->mPort,
- BufferToString(aBuffer, aLength));
+ LogDebg("EnqueuePacket([%s]:%u) - %s", Ip6AddrToString(&aDestSockAddr->mAddress), aDestSockAddr->mPort,
+ BufferToString(aBuffer, aLength));
exit:
return;
@@ -518,7 +566,14 @@
void platformTrelInit(const char *aTrelUrl)
{
- otLogDebgPlat("[trel] platformTrelInit(aTrelUrl:\"%s\")", aTrelUrl != nullptr ? aTrelUrl : "");
+ // To silence "unused function" warning.
+ (void)LogCrit;
+ (void)LogWarn;
+ (void)LogInfo;
+ (void)LogNote;
+ (void)LogDebg;
+
+ LogDebg("platformTrelInit(aTrelUrl:\"%s\")", aTrelUrl != nullptr ? aTrelUrl : "");
assert(!sInitialized);
@@ -545,7 +600,7 @@
otPlatTrelDisable(nullptr);
sInterfaceName[0] = '\0';
sInitialized = false;
- otLogDebgPlat("[trel] platformTrelDeinit()");
+ LogDebg("platformTrelDeinit()");
exit:
return;
diff --git a/src/posix/platform/udp.cpp b/src/posix/platform/udp.cpp
index 4161672..89c6d3d 100644
--- a/src/posix/platform/udp.cpp
+++ b/src/posix/platform/udp.cpp
@@ -69,10 +69,6 @@
int FdFromHandle(void *aHandle) { return static_cast<int>(reinterpret_cast<long>(aHandle)); }
-bool IsLinkLocal(const struct in6_addr &aAddress) { return aAddress.s6_addr[0] == 0xfe && aAddress.s6_addr[1] == 0x80; }
-
-bool IsMulticast(const otIp6Address &aAddress) { return aAddress.mFields.m8[0] == 0xff; }
-
otError transmitPacket(int aFd, uint8_t *aPayload, uint16_t aLength, const otMessageInfo &aMessageInfo)
{
#ifdef __APPLE__
@@ -93,9 +89,9 @@
memset(&peerAddr, 0, sizeof(peerAddr));
peerAddr.sin6_port = htons(aMessageInfo.mPeerPort);
peerAddr.sin6_family = AF_INET6;
- memcpy(&peerAddr.sin6_addr, &aMessageInfo.mPeerAddr, sizeof(peerAddr.sin6_addr));
+ CopyIp6AddressTo(aMessageInfo.mPeerAddr, &peerAddr.sin6_addr);
- if (IsLinkLocal(peerAddr.sin6_addr) && !aMessageInfo.mIsHostInterface)
+ if (IsIp6AddressLinkLocal(aMessageInfo.mPeerAddr) && !aMessageInfo.mIsHostInterface)
{
// sin6_scope_id only works for link local destinations
peerAddr.sin6_scope_id = gNetifIndex;
@@ -127,8 +123,7 @@
controlLength += CMSG_SPACE(sizeof(int));
}
- if (!IsMulticast(aMessageInfo.mSockAddr) &&
- memcmp(&aMessageInfo.mSockAddr, &in6addr_any, sizeof(aMessageInfo.mSockAddr)))
+ if (!IsIp6AddressMulticast(aMessageInfo.mSockAddr) && !IsIp6AddressUnspecified(aMessageInfo.mSockAddr))
{
struct in6_pktinfo pktinfo;
@@ -139,7 +134,7 @@
pktinfo.ipi6_ifindex = aMessageInfo.mIsHostInterface ? 0 : gNetifIndex;
- memcpy(&pktinfo.ipi6_addr, &aMessageInfo.mSockAddr, sizeof(pktinfo.ipi6_addr));
+ CopyIp6AddressTo(aMessageInfo.mSockAddr, &pktinfo.ipi6_addr);
memcpy(CMSG_DATA(cmsg), &pktinfo, sizeof(pktinfo));
controlLength += CMSG_SPACE(sizeof(pktinfo));
@@ -206,13 +201,13 @@
memcpy(&pktinfo, CMSG_DATA(cmsg), sizeof(pktinfo));
aMessageInfo.mIsHostInterface = (pktinfo.ipi6_ifindex != gNetifIndex);
- memcpy(&aMessageInfo.mSockAddr, &pktinfo.ipi6_addr, sizeof(aMessageInfo.mSockAddr));
+ ReadIp6AddressFrom(&pktinfo.ipi6_addr, aMessageInfo.mSockAddr);
}
}
}
aMessageInfo.mPeerPort = ntohs(peerAddr.sin6_port);
- memcpy(&aMessageInfo.mPeerAddr, &peerAddr.sin6_addr, sizeof(aMessageInfo.mPeerAddr));
+ ReadIp6AddressFrom(&peerAddr.sin6_addr, aMessageInfo.mPeerAddr);
exit:
return rval > 0 ? OT_ERROR_NONE : OT_ERROR_FAILED;
@@ -270,7 +265,8 @@
memset(&sin6, 0, sizeof(struct sockaddr_in6));
sin6.sin6_port = htons(aUdpSocket->mSockName.mPort);
sin6.sin6_family = AF_INET6;
- memcpy(&sin6.sin6_addr, &aUdpSocket->mSockName.mAddress, sizeof(sin6.sin6_addr));
+ CopyIp6AddressTo(aUdpSocket->mSockName.mAddress, &sin6.sin6_addr);
+
VerifyOrExit(0 == bind(fd, reinterpret_cast<struct sockaddr *>(&sin6), sizeof(sin6)), error = OT_ERROR_FAILED);
}
@@ -283,7 +279,7 @@
exit:
if (error == OT_ERROR_FAILED)
{
- otLogCritPlat("Failed to bind UDP socket: %s", strerror(errno));
+ ot::Posix::Udp::LogCrit("Failed to bind UDP socket: %s", strerror(errno));
}
return error;
@@ -325,7 +321,7 @@
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
if (otSysGetInfraNetifName() == nullptr || otSysGetInfraNetifName()[0] == '\0')
{
- otLogWarnPlat("No backbone interface given, %s fails.", __func__);
+ ot::Posix::Udp::LogWarn("No backbone interface given, %s fails.", __func__);
ExitNow(error = OT_ERROR_INVALID_ARGS);
}
#ifdef __linux__
@@ -358,8 +354,7 @@
otError error = OT_ERROR_NONE;
struct sockaddr_in6 sin6;
int fd;
- bool isDisconnect = memcmp(&aUdpSocket->mPeerName.mAddress, &in6addr_any, sizeof(in6addr_any)) == 0 &&
- aUdpSocket->mPeerName.mPort == 0;
+ bool isDisconnect = IsIp6AddressUnspecified(aUdpSocket->mPeerName.mAddress) && (aUdpSocket->mPeerName.mPort == 0);
VerifyOrExit(aUdpSocket->mHandle != nullptr, error = OT_ERROR_INVALID_ARGS);
@@ -367,10 +362,11 @@
memset(&sin6, 0, sizeof(struct sockaddr_in6));
sin6.sin6_port = htons(aUdpSocket->mPeerName.mPort);
+
if (!isDisconnect)
{
sin6.sin6_family = AF_INET6;
- memcpy(&sin6.sin6_addr, &aUdpSocket->mPeerName.mAddress, sizeof(sin6.sin6_addr));
+ CopyIp6AddressTo(aUdpSocket->mPeerName.mAddress, &sin6.sin6_addr);
}
else
{
@@ -382,7 +378,7 @@
if (getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &netifName, &len) != 0)
{
- otLogWarnPlat("Failed to read socket bound device: %s", strerror(errno));
+ ot::Posix::Udp::LogWarn("Failed to read socket bound device: %s", strerror(errno));
len = 0;
}
@@ -396,7 +392,7 @@
{
fd = FdFromHandle(aUdpSocket->mHandle);
VerifyOrExit(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &netifName, len) == 0, {
- otLogWarnPlat("Failed to bind to device: %s", strerror(errno));
+ ot::Posix::Udp::LogWarn("Failed to bind to device: %s", strerror(errno));
error = OT_ERROR_FAILED;
});
}
@@ -410,8 +406,9 @@
#ifdef __APPLE__
VerifyOrExit(errno == EAFNOSUPPORT && isDisconnect);
#endif
- otLogWarnPlat("Failed to connect to [%s]:%u: %s", Ip6AddressString(&aUdpSocket->mPeerName.mAddress).AsCString(),
- aUdpSocket->mPeerName.mPort, strerror(errno));
+ ot::Posix::Udp::LogWarn("Failed to connect to [%s]:%u: %s",
+ Ip6AddressString(&aUdpSocket->mPeerName.mAddress).AsCString(),
+ aUdpSocket->mPeerName.mPort, strerror(errno));
error = OT_ERROR_FAILED;
}
@@ -468,7 +465,7 @@
VerifyOrExit(aUdpSocket->mHandle != nullptr, error = OT_ERROR_INVALID_ARGS);
fd = FdFromHandle(aUdpSocket->mHandle);
- memcpy(&mreq.ipv6mr_multiaddr, aAddress->mFields.m8, sizeof(mreq.ipv6mr_multiaddr));
+ CopyIp6AddressTo(*aAddress, &mreq.ipv6mr_multiaddr);
switch (aNetifIdentifier)
{
@@ -492,8 +489,9 @@
exit:
if (error != OT_ERROR_NONE)
{
- otLogCritPlat("IPV6_JOIN_GROUP failed: %s", strerror(errno));
+ ot::Posix::Udp::LogCrit("IPV6_JOIN_GROUP failed: %s", strerror(errno));
}
+
return error;
}
@@ -508,7 +506,7 @@
VerifyOrExit(aUdpSocket->mHandle != nullptr, error = OT_ERROR_INVALID_ARGS);
fd = FdFromHandle(aUdpSocket->mHandle);
- memcpy(&mreq.ipv6mr_multiaddr, aAddress->mFields.m8, sizeof(mreq.ipv6mr_multiaddr));
+ CopyIp6AddressTo(*aAddress, &mreq.ipv6mr_multiaddr);
switch (aNetifIdentifier)
{
@@ -532,14 +530,17 @@
exit:
if (error != OT_ERROR_NONE)
{
- otLogCritPlat("IPV6_LEAVE_GROUP failed: %s", strerror(errno));
+ ot::Posix::Udp::LogCrit("IPV6_LEAVE_GROUP failed: %s", strerror(errno));
}
+
return error;
}
namespace ot {
namespace Posix {
+const char Udp::kLogModuleName[] = "Udp";
+
void Udp::Update(otSysMainloopContext &aContext)
{
VerifyOrExit(gNetifIndex != 0);
diff --git a/src/posix/platform/udp.hpp b/src/posix/platform/udp.hpp
index adc252a..f22a8cf 100644
--- a/src/posix/platform/udp.hpp
+++ b/src/posix/platform/udp.hpp
@@ -29,14 +29,18 @@
#define OT_POSIX_PLATFORM_UDP_HPP_
#include "core/common/non_copyable.hpp"
-#include "posix/platform/mainloop.hpp"
+
+#include "logger.hpp"
+#include "mainloop.hpp"
namespace ot {
namespace Posix {
-class Udp : public Mainloop::Source, private NonCopyable
+class Udp : public Mainloop::Source, public Logger<Udp>, private NonCopyable
{
public:
+ static const char kLogModuleName[];
+
static Udp &Get(void);
void Init(const char *aIfName);
diff --git a/src/posix/platform/vendor_interface.hpp b/src/posix/platform/vendor_interface.hpp
index 4ee7a1a..449ebe5 100644
--- a/src/posix/platform/vendor_interface.hpp
+++ b/src/posix/platform/vendor_interface.hpp
@@ -31,8 +31,8 @@
* This file includes definitions for the vendor interface to radio (RCP).
*/
-#ifndef POSIX_APP_VENDOR_INTERFACE_HPP_
-#define POSIX_APP_VENDOR_INTERFACE_HPP_
+#ifndef OT_POSIX_APP_VENDOR_INTERFACE_HPP_
+#define OT_POSIX_APP_VENDOR_INTERFACE_HPP_
#include "openthread-posix-config.h"
@@ -171,4 +171,4 @@
} // namespace Posix
} // namespace ot
-#endif // POSIX_APP_VENDOR_INTERFACE_HPP_
+#endif // OT_POSIX_APP_VENDOR_INTERFACE_HPP_
diff --git a/tests/scripts/expect/cli-tcat.exp b/tests/scripts/expect/cli-tcat.exp
new file mode 100755
index 0000000..00ce087
--- /dev/null
+++ b/tests/scripts/expect/cli-tcat.exp
@@ -0,0 +1,61 @@
+#!/usr/bin/expect -f
+#
+# Copyright (c) 2022, The OpenThread Authors.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+source "tests/scripts/expect/_common.exp"
+
+spawn_node 1 "cli"
+
+switch_node 1
+send "tcat start\n"
+expect_line "Done"
+
+spawn python "tools/tcat_ble_client/bbtc.py" --simulation 1 --cert_path "tools/tcat_ble_client/auth"
+set py_client "$spawn_id"
+expect_line "Done"
+send "commission\n"
+expect_line "\tTYPE:\tRESPONSE_W_STATUS"
+expect_line "\tVALUE:\t0x00"
+
+send "thread start\n"
+expect_line "\tTYPE:\tRESPONSE_W_STATUS"
+expect_line "\tVALUE:\t0x00"
+
+send "exit\n"
+expect eof
+
+switch_node 1
+send "tcat stop\n"
+expect_line "Done"
+
+send "networkkey\n"
+expect_line "fda7c771a27202e232ecd04cf934f476"
+expect_line "Done"
+
+wait_for "state" "leader"
+expect_line "Done"
diff --git a/tests/scripts/expect/posix-rcp-local-host.exp b/tests/scripts/expect/posix-rcp-local-host.exp
new file mode 100755
index 0000000..e8c8201
--- /dev/null
+++ b/tests/scripts/expect/posix-rcp-local-host.exp
@@ -0,0 +1,44 @@
+#!/usr/bin/expect -f
+#
+# 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.
+#
+
+source "tests/scripts/expect/_common.exp"
+
+spawn_node 1 rcp "spinel+hdlc+uart://$::env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=-Llo&forkpty-arg=1"
+send "factoryreset\n"
+wait_for "state" "disabled"
+setup_default_network
+attach
+
+spawn_node 2 rcp "spinel+hdlc+uart://$::env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=--local-host=127.0.0.1&forkpty-arg=2"
+send "factoryreset\n"
+wait_for "state" "disabled"
+setup_default_network
+attach child
+
+dispose_all
diff --git a/tests/scripts/thread-cert/addon_test_channel_manager_autocsl.py b/tests/scripts/thread-cert/addon_test_channel_manager_autocsl.py
new file mode 100755
index 0000000..2eaff4f
--- /dev/null
+++ b/tests/scripts/thread-cert/addon_test_channel_manager_autocsl.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2023, The OpenThread Authors.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+import unittest
+
+import config
+import mle
+import thread_cert
+from pktverify import consts
+
+LEADER = 1
+ED = 2
+SSED = 3
+
+
+class SSED_CslChannelManager(thread_cert.TestCase):
+ TOPOLOGY = {
+ LEADER: {
+ 'version': '1.2',
+ },
+ ED: {
+ 'version': '1.2',
+ 'is_mtd': False,
+ 'mode': 'rn',
+ },
+ SSED: {
+ 'version': '1.2',
+ 'is_mtd': True,
+ 'mode': '-',
+ },
+ }
+ """All nodes are created with default configurations"""
+
+ def test(self):
+
+ self.nodes[SSED].set_csl_period(consts.CSL_DEFAULT_PERIOD)
+ self.nodes[SSED].set_csl_timeout(consts.CSL_DEFAULT_TIMEOUT)
+
+ self.nodes[SSED].get_csl_info()
+
+ self.nodes[LEADER].start()
+ self.simulator.go(config.LEADER_STARTUP_DELAY)
+ self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
+ channel = self.nodes[LEADER].get_channel()
+
+ self.nodes[SSED].start()
+ self.simulator.go(7)
+ self.assertEqual(self.nodes[SSED].get_state(), 'child')
+
+ csl_channel = 0
+ csl_config = self.nodes[SSED].get_csl_info()
+ self.assertTrue(int(csl_config['channel']) == csl_channel)
+ self.assertTrue(csl_config['period'] == '500000us')
+
+ print('SSED rloc:%s' % self.nodes[SSED].get_rloc())
+ self.assertTrue(self.nodes[LEADER].ping(self.nodes[SSED].get_rloc()))
+
+ # let channel monitor collect >970 sample counts
+ self.simulator.go(980 * 41)
+ results = self.nodes[SSED].get_channel_monitor_info()
+ self.assertTrue(int(results['count']) > 970)
+
+ # Configure channel manager channel masks
+ # Set cca threshold to 0 as we cannot change cca assessment in simulation.
+ # and shorten interval to speedup test
+ all_channels_mask = int('0x7fff800', 0)
+ chan_12_to_15_mask = int('0x000f000', 0)
+ interval = 30
+ self.nodes[SSED].set_channel_manager_supported(all_channels_mask)
+ self.nodes[SSED].set_channel_manager_favored(chan_12_to_15_mask)
+ self.nodes[SSED].set_channel_manager_cca_threshold('0x0000')
+ self.nodes[SSED].set_channel_manager_interval(interval)
+
+ # enable channel manager auto-select and check
+ # network channel is not changed by channel manager on SSED
+ # and also csl_channel is unchanged
+ self.nodes[SSED].set_channel_manager_auto_enable(True)
+ self.simulator.go(interval + 1)
+ results = self.nodes[SSED].get_channel_manager_config()
+ self.assertTrue(int(results['auto']) == 1)
+ self.assertTrue(results['cca threshold'] == '0x0000')
+ self.assertTrue(int(results['interval']) == interval)
+ self.assertTrue('11-26' in results['supported'])
+ self.simulator.go(1)
+ self.assertTrue(self.nodes[SSED].get_channel() == channel)
+ csl_config = self.nodes[SSED].get_csl_info()
+ self.assertTrue(int(csl_config['channel']) == csl_channel)
+
+ # check SSED can change csl channel
+ csl_channel = 25
+ self.flush_all()
+ self.nodes[SSED].set_csl_channel(csl_channel)
+ self.simulator.go(1)
+ ssed_messages = self.simulator.get_messages_sent_by(SSED)
+ self.assertIsNotNone(ssed_messages.next_mle_message(mle.CommandType.CHILD_UPDATE_REQUEST))
+ self.simulator.go(1)
+ csl_config = self.nodes[SSED].get_csl_info()
+ self.assertTrue(int(csl_config['channel']) == csl_channel)
+ self.simulator.go(1)
+ self.assertTrue(self.nodes[LEADER].ping(self.nodes[SSED].get_rloc()))
+
+ # enable channel manager autocsl-select in addition
+ # and check csl channel changed to best favored channel 12
+ csl_channel = 12
+ self.nodes[SSED].set_channel_manager_autocsl_enable(True)
+ self.simulator.go(interval + 1)
+ results = self.nodes[SSED].get_channel_manager_config()
+ self.assertTrue(int(results['autocsl']) == 1)
+ self.assertTrue(int(results['channel']) == csl_channel)
+ csl_config = self.nodes[SSED].get_csl_info()
+ self.assertTrue(int(csl_config['channel']) == csl_channel)
+ self.simulator.go(1)
+ self.assertTrue(self.nodes[LEADER].ping(self.nodes[SSED].get_rloc()))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/scripts/thread-cert/border_router/test_advertising_proxy.py b/tests/scripts/thread-cert/border_router/test_advertising_proxy.py
index 5c66bf9..2ae828b 100755
--- a/tests/scripts/thread-cert/border_router/test_advertising_proxy.py
+++ b/tests/scripts/thread-cert/border_router/test_advertising_proxy.py
@@ -27,7 +27,6 @@
# POSSIBILITY OF SUCH DAMAGE.
#
import ipaddress
-import logging
import unittest
import config
@@ -81,6 +80,12 @@
host.start(start_radvd=False)
self.simulator.go(5)
+ # Reserve UDP ports to verify that SRP server can skip the unavailable
+ # ports correctly
+ server.reserve_udp_port(53535)
+ server.reserve_udp_port(53536)
+ server.reserve_udp_port(53537)
+
self.assertEqual(server.srp_server_get_state(), 'disabled')
server.srp_server_set_enabled(True)
server.srp_server_set_lease_range(LEASE, LEASE, KEY_LEASE, KEY_LEASE)
@@ -88,6 +93,7 @@
self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY)
self.assertEqual('leader', server.get_state())
self.assertEqual(server.srp_server_get_state(), 'running')
+ self.assertNotIn(server.get_srp_server_port(), [53535, 53536, 53537])
client.start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
diff --git a/tests/scripts/thread-cert/border_router/test_manual_maddress.py b/tests/scripts/thread-cert/border_router/test_manual_maddress.py
index 536d3ad..78edac8 100755
--- a/tests/scripts/thread-cert/border_router/test_manual_maddress.py
+++ b/tests/scripts/thread-cert/border_router/test_manual_maddress.py
@@ -86,11 +86,12 @@
# TD registers for multicast address, MA1, at BR_1.
td.add_ipmaddr_tun(MA1)
- self.simulator.go(5)
+ self.simulator.go(10)
# Host sends a ping packet to the multicast address, MA1. TD should
# respond to the ping request.
- host.ping(MA1, backbone=True, ttl=10, interface=host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])
+ self.assertTrue(
+ host.ping(MA1, backbone=True, ttl=10, interface=host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
self.simulator.go(5)
def verify(self, pv: pktverify.packet_verifier.PacketVerifier):
diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py
index 5359848..9f5d337 100755
--- a/tests/scripts/thread-cert/node.py
+++ b/tests/scripts/thread-cert/node.py
@@ -197,6 +197,9 @@
self.pexpect.wait()
self.pexpect.proc.kill()
+ def reserve_udp_port(self, port):
+ self.bash(f'socat -u UDP6-LISTEN:{port},bindtodevice=wpan0 - &')
+
def destroy(self):
logging.info("Destroying %s", self)
self._shutdown_docker()
@@ -807,6 +810,18 @@
results = [line for line in output if self._match_pattern(line, pattern)]
return results
+ def _expect_key_value_pairs(self, pattern, separator=': '):
+ """Expect 'key: value' in multiple lines.
+
+ Returns:
+ Dictionary of the key:value pairs.
+ """
+ result = {}
+ for line in self._expect_results(pattern):
+ key, val = line.split(separator)
+ result.update({key: val})
+ return result
+
@staticmethod
def _match_pattern(line, pattern):
if isinstance(pattern, str):
@@ -1700,6 +1715,10 @@
self.send_command(cmd)
self._expect_done()
+ def get_key_switch_guardtime(self):
+ self.send_command('keysequence guardtime')
+ return int(self._expect_result(r'\d+'))
+
def set_key_switch_guardtime(self, key_switch_guardtime):
cmd = 'keysequence guardtime %d' % key_switch_guardtime
self.send_command(cmd)
@@ -1795,7 +1814,7 @@
def get_csl_info(self):
self.send_command('csl')
- self._expect_done()
+ return self._expect_key_value_pairs(r'\S+')
def set_csl_channel(self, csl_channel):
self.send_command('csl channel %d' % csl_channel)
@@ -2683,7 +2702,7 @@
self.send_command('dataset commit pending')
self._expect_done()
- def start_dataset_updater(self, panid=None, channel=None):
+ def start_dataset_updater(self, panid=None, channel=None, security_policy=None, delay=None):
self.send_command('dataset clear')
self._expect_done()
@@ -2697,6 +2716,18 @@
self.send_command(cmd)
self._expect_done()
+ if security_policy is not None:
+ cmd = 'dataset securitypolicy %d %s ' % (security_policy[0], security_policy[1])
+ if (len(security_policy) >= 3):
+ cmd += '%d ' % (security_policy[2])
+ self.send_command(cmd)
+ self._expect_done()
+
+ if delay is not None:
+ cmd = 'dataset delay %d ' % delay
+ self.send_command(cmd)
+ self._expect_done()
+
self.send_command('dataset updater start')
self._expect_done()
@@ -3582,6 +3613,81 @@
line = self._expect_command_output()[0]
return [int(item) for item in line.split()]
+ def get_channel_monitor_info(self) -> Dict:
+ """
+ Returns:
+ Dict of channel monitor info, e.g.
+ {'enabled': '1',
+ 'interval': '41000',
+ 'threshold': '-75',
+ 'window': '960',
+ 'count': '985',
+ 'occupancies': {
+ '11': '0.00%',
+ '12': '3.50%',
+ '13': '9.89%',
+ '14': '15.36%',
+ '15': '20.02%',
+ '16': '21.95%',
+ '17': '32.71%',
+ '18': '35.76%',
+ '19': '37.97%',
+ '20': '43.68%',
+ '21': '48.95%',
+ '22': '54.05%',
+ '23': '58.65%',
+ '24': '68.26%',
+ '25': '66.73%',
+ '26': '73.12%'
+ }
+ }
+ """
+ config = {}
+ self.send_command('channel monitor')
+
+ for line in self._expect_results(r'\S+'):
+ if re.match(r'.*:\s.*', line):
+ key, val = line.split(':')
+ config.update({key: val.strip()})
+ elif re.match(r'.*:', line): # occupancy
+ occ_key, val = line.split(':')
+ val = {}
+ config.update({occ_key: val})
+ elif 'busy' in line:
+ # channel occupancies
+ key = line.split()[1]
+ val = line.split()[3]
+ config[occ_key].update({key: val})
+ return config
+
+ def set_channel_manager_auto_enable(self, enable: bool):
+ self.send_command(f'channel manager auto {int(enable)}')
+ self._expect_done()
+
+ def set_channel_manager_autocsl_enable(self, enable: bool):
+ self.send_command(f'channel manager autocsl {int(enable)}')
+ self._expect_done()
+
+ def set_channel_manager_supported(self, channel_mask: int):
+ self.send_command(f'channel manager supported {int(channel_mask)}')
+ self._expect_done()
+
+ def set_channel_manager_favored(self, channel_mask: int):
+ self.send_command(f'channel manager favored {int(channel_mask)}')
+ self._expect_done()
+
+ def set_channel_manager_interval(self, interval: int):
+ self.send_command(f'channel manager interval {interval}')
+ self._expect_done()
+
+ def set_channel_manager_cca_threshold(self, hex_value: str):
+ self.send_command(f'channel manager threshold {hex_value}')
+ self._expect_done()
+
+ def get_channel_manager_config(self):
+ self.send_command('channel manager')
+ return self._expect_key_value_pairs(r'\S+')
+
class Node(NodeImpl, OtCli):
pass
diff --git a/tests/scripts/thread-cert/test_detach.py b/tests/scripts/thread-cert/test_detach.py
index 954df98..c32f8b3 100755
--- a/tests/scripts/thread-cert/test_detach.py
+++ b/tests/scripts/thread-cert/test_detach.py
@@ -105,7 +105,7 @@
self.assertFalse(list(filter(lambda x: x[1]['rloc16'] == router1_rloc16, leader.router_table().items())))
router1.start()
- self.simulator.go(5)
+ self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(router1.get_state(), 'router')
child1.start()
@@ -121,7 +121,7 @@
self.assertEqual(child1.get_state(), 'disabled')
router1.start()
- self.simulator.go(5)
+ self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(router1.get_state(), 'router')
child1.start()
diff --git a/tests/scripts/thread-cert/test_key_rotation_and_key_guard_time.py b/tests/scripts/thread-cert/test_key_rotation_and_key_guard_time.py
new file mode 100755
index 0000000..595ffea
--- /dev/null
+++ b/tests/scripts/thread-cert/test_key_rotation_and_key_guard_time.py
@@ -0,0 +1,171 @@
+#!/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.
+#
+
+import ipaddress
+import unittest
+import math
+
+import command
+import config
+import thread_cert
+
+# Test description:
+#
+# This test verifies key rotation and key guard time mechanisms.
+#
+#
+# Topology:
+#
+# leader --- router
+# | \
+# | \
+# child reed
+#
+
+LEADER = 1
+CHILD = 2
+REED = 3
+ROUTER = 4
+
+
+class MleMsgKeySeqJump(thread_cert.TestCase):
+ USE_MESSAGE_FACTORY = False
+ SUPPORT_NCP = False
+
+ TOPOLOGY = {
+ LEADER: {
+ 'name': 'LEADER',
+ 'mode': 'rdn',
+ },
+ CHILD: {
+ 'name': 'CHILD',
+ 'is_mtd': True,
+ 'mode': 'rn',
+ },
+ REED: {
+ 'name': 'REED',
+ 'mode': 'rn'
+ },
+ ROUTER: {
+ 'name': 'ROUTER',
+ 'mode': 'rdn',
+ },
+ }
+
+ def test(self):
+ leader = self.nodes[LEADER]
+ child = self.nodes[CHILD]
+ reed = self.nodes[REED]
+ router = self.nodes[ROUTER]
+
+ nodes = [leader, child, reed, router]
+
+ #-------------------------------------------------------------------
+ # Form the network.
+
+ for node in nodes:
+ node.set_key_sequence_counter(0)
+
+ leader.start()
+ self.simulator.go(config.LEADER_STARTUP_DELAY)
+ self.assertEqual(leader.get_state(), 'leader')
+
+ child.start()
+ reed.start()
+ self.simulator.go(5)
+ self.assertEqual(child.get_state(), 'child')
+ self.assertEqual(reed.get_state(), 'child')
+
+ router.start()
+ self.simulator.go(config.ROUTER_STARTUP_DELAY)
+ self.assertEqual(router.get_state(), 'router')
+
+ #-------------------------------------------------------------------
+ # Validate the initial key seq counter and key switch guard time
+
+ for node in nodes:
+ self.assertEqual(node.get_key_sequence_counter(), 0)
+ self.assertEqual(node.get_key_switch_guardtime(), 624)
+
+ #-------------------------------------------------------------------
+ # Change the key rotation time a bunch of times and make sure that
+ # the key switch guard time is properly changed (should be set
+ # to 93% of the rotation time).
+
+ for rotation_time in [100, 1, 10, 888, 2]:
+ reed.start_dataset_updater(security_policy=[rotation_time, 'onrc'])
+ guardtime = math.floor(rotation_time * 93 / 100) if rotation_time >= 2 else 1
+ self.simulator.go(100)
+ for node in nodes:
+ self.assertEqual(node.get_key_switch_guardtime(), guardtime)
+
+ #-------------------------------------------------------------------
+ # Wait for key rotation time (2 hours) and check that all nodes
+ # moved to the next key seq counter
+
+ self.simulator.go(2 * 60 * 60)
+ for node in nodes:
+ self.assertEqual(node.get_key_sequence_counter(), 1)
+
+ #-------------------------------------------------------------------
+ # Manually increment the key sequence counter on leader and make
+ # sure other nodes are not updated due to key guard time.
+
+ router.set_key_sequence_counter(2)
+
+ self.simulator.go(50 * 60)
+
+ self.assertEqual(router.get_key_sequence_counter(), 2)
+
+ for node in [leader, reed, child]:
+ self.assertEqual(node.get_key_sequence_counter(), 1)
+
+ #-------------------------------------------------------------------
+ # Make sure nodes can communicate with each other.
+
+ self.assertTrue(leader.ping(router.get_mleid()))
+ self.assertTrue(router.ping(child.get_mleid()))
+
+ #-------------------------------------------------------------------
+ # Wait for rotation time to expire. Validate that the `router`
+ # has moved to key seq `3` and all other nodes also followed.
+
+ self.simulator.go(75 * 60)
+
+ self.assertEqual(router.get_key_sequence_counter(), 3)
+
+ for node in nodes:
+ self.assertEqual(node.get_key_sequence_counter(), 3)
+
+ self.assertTrue(leader.ping(router.get_mleid()))
+ self.assertTrue(router.ping(child.get_mleid()))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/scripts/thread-cert/test_mle_msg_key_seq_jump.py b/tests/scripts/thread-cert/test_mle_msg_key_seq_jump.py
index d400557..2463434 100755
--- a/tests/scripts/thread-cert/test_mle_msg_key_seq_jump.py
+++ b/tests/scripts/thread-cert/test_mle_msg_key_seq_jump.py
@@ -221,20 +221,20 @@
self.assertEqual(reed.get_key_sequence_counter(), 20)
#-------------------------------------------------------------------
- # Move forward the key seq counter by one on router. Wait for max
+ # Move forward the key seq counter by two on router. Wait for max
# time between advertisements. Validate that leader adopts the higher
# counter value.
- router.set_key_sequence_counter(21)
- self.assertEqual(router.get_key_sequence_counter(), 21)
+ router.set_key_sequence_counter(22)
+ self.assertEqual(router.get_key_sequence_counter(), 22)
self.simulator.go(52)
- self.assertEqual(leader.get_key_sequence_counter(), 21)
- self.assertEqual(reed.get_key_sequence_counter(), 21)
+ self.assertEqual(leader.get_key_sequence_counter(), 22)
+ self.assertEqual(reed.get_key_sequence_counter(), 22)
child.set_mode('r')
self.simulator.go(2)
- self.assertEqual(child.get_key_sequence_counter(), 21)
+ self.assertEqual(child.get_key_sequence_counter(), 22)
#-------------------------------------------------------------------
# Force a reattachment from the child with a higher key seq counter,
@@ -247,6 +247,7 @@
child.factory_reset()
self.assertEqual(child.get_state(), 'disabled')
+ child.set_mode('r')
child.set_active_dataset(channel=leader.get_channel(),
network_key=leader.get_networkkey(),
diff --git a/tests/scripts/thread-cert/test_netdata_publisher.py b/tests/scripts/thread-cert/test_netdata_publisher.py
index 7192e6f..c31ac6e 100755
--- a/tests/scripts/thread-cert/test_netdata_publisher.py
+++ b/tests/scripts/thread-cert/test_netdata_publisher.py
@@ -70,7 +70,7 @@
# The desired number of entries (based on related config).
DESIRED_NUM_DNSSRP_ANYCAST = 8
-DESIRED_NUM_DNSSRP_UNCIAST = 2
+DESIRED_NUM_DNSSRP_UNICAST = 2
DESIRED_NUM_ON_MESH_PREFIX = 3
DESIRED_NUM_EXTERNAL_ROUTE = 10
@@ -263,52 +263,39 @@
self.verify_anycast_services(services)
#---------------------------------------------------------------------------------
- # DNS/SRP unicast entries
+ # DNS/SRP service data unicast entries
- # Publish DNS/SRP unicast address on all routers, first using
- # MLE-EID address, then change to use specific address. Verify
- # that number of entries in network data is correct in each step
- # and that entries are switched correctly.
num = 0
for node in routers:
- node.netdata_publish_dnssrp_unicast_mleid(DNSSRP_PORT)
- self.simulator.go(WAIT_TIME)
- num += 1
- services = leader.get_services()
- self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNCIAST))
- self.verify_unicast_services(services)
-
- for node in routers:
node.netdata_publish_dnssrp_unicast(DNSSRP_ADDRESS, DNSSRP_PORT)
self.simulator.go(WAIT_TIME)
+ num += 1
services = leader.get_services()
- self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNCIAST))
+ self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNICAST))
self.verify_unicast_services(services)
for node in routers:
node.srp_server_set_enabled(True)
self.simulator.go(WAIT_TIME)
+
self.assertEqual(sum(node.srp_server_get_state() == 'running' for node in routers),
- min(len(routers), DESIRED_NUM_DNSSRP_UNCIAST))
+ min(len(routers), DESIRED_NUM_DNSSRP_UNICAST))
self.assertEqual(sum(node.srp_server_get_state() == 'stopped' for node in routers),
- max(len(routers) - DESIRED_NUM_DNSSRP_UNCIAST, 0))
+ max(len(routers) - DESIRED_NUM_DNSSRP_UNICAST, 0))
for node in routers:
node.netdata_unpublish_dnssrp()
self.simulator.go(WAIT_TIME)
num -= 1
services = leader.get_services()
- self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNCIAST))
+ self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNICAST))
self.verify_unicast_services(services)
for node in routers:
node.srp_server_set_enabled(False)
self.assertEqual(node.srp_server_get_state(), 'disabled')
#---------------------------------------------------------------------------------
- # DNS/SRP unicast and anycast entry
-
- # Verify that publishing an anycast entry will update the limit
- # for the unicast MLE-EID address entry and all are removed.
+ # DNS/SRP server data unicast entries
num = 0
for node in routers:
@@ -316,20 +303,77 @@
self.simulator.go(WAIT_TIME)
num += 1
services = leader.get_services()
- self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNCIAST))
+ self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNICAST))
self.verify_unicast_services(services)
+ for node in routers:
+ node.srp_server_set_enabled(True)
+ self.simulator.go(WAIT_TIME)
+ self.assertEqual(sum(node.srp_server_get_state() == 'running' for node in routers),
+ min(len(routers), DESIRED_NUM_DNSSRP_UNICAST))
+ self.assertEqual(sum(node.srp_server_get_state() == 'stopped' for node in routers),
+ max(len(routers) - DESIRED_NUM_DNSSRP_UNICAST, 0))
+
+ for node in routers:
+ node.netdata_unpublish_dnssrp()
+ self.simulator.go(WAIT_TIME)
+ num -= 1
+ services = leader.get_services()
+ self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNICAST))
+ self.verify_unicast_services(services)
+ for node in routers:
+ node.srp_server_set_enabled(False)
+ self.assertEqual(node.srp_server_get_state(), 'disabled')
+
+ #---------------------------------------------------------------------------------
+ # DNS/SRP server data unicast vs anycast
+
+ num = 0
+ for node in routers:
+ node.netdata_publish_dnssrp_unicast_mleid(DNSSRP_PORT)
+ self.simulator.go(WAIT_TIME)
+ num += 1
+ services = leader.get_services()
+ self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNICAST))
+ self.verify_unicast_services(services)
+
+ # Verify that publishing an anycast entry will update the
+ # limit for the server data unicast address entry and all are
+ # removed.
+
leader.netdata_publish_dnssrp_anycast(ANYCAST_SEQ_NUM)
self.simulator.go(WAIT_TIME)
services = leader.get_services()
self.assertEqual(len(services), 1)
self.verify_anycast_services(services)
+ # Removing the anycast entry will cause the lower priority
+ # server data unicast entries to be added again.
+
leader.netdata_unpublish_dnssrp()
self.simulator.go(WAIT_TIME)
services = leader.get_services()
- self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNCIAST))
+ self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNICAST))
+ self.verify_unicast_services(services)
+
+ #---------------------------------------------------------------------------------
+ # DNS/SRP server data unicast vs service data unicast
+
+ leader.netdata_publish_dnssrp_unicast(DNSSRP_ADDRESS, DNSSRP_PORT)
+ self.simulator.go(WAIT_TIME)
+ services = leader.get_services()
+ self.assertEqual(len(services), 1)
+ self.verify_unicast_services(services)
+
+ # Removing the service data unicast entry will cause the lower
+ # priority server data unicast entries to be added again.
+
+ leader.netdata_unpublish_dnssrp()
+ self.simulator.go(WAIT_TIME)
+
+ services = leader.get_services()
+ self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNICAST))
self.verify_unicast_services(services)
for node in routers:
@@ -494,15 +538,15 @@
# Replace the published route on leader with '::/0'.
leader.netdata_publish_replace(EXTERNAL_ROUTE, '::/0', EXTERNAL_FLAGS, 'med')
- self.simulator.go(0.2)
+ self.simulator.go(1)
routes = leader.get_routes()
self.assertEqual([route.split(' ')[0] == '::/0' for route in routes].count(True), 1)
- self.check_num_of_routes(routes, num - 1, 1, 0)
# Replace it back to the original route.
leader.netdata_publish_replace('::/0', EXTERNAL_ROUTE, EXTERNAL_FLAGS, 'high')
self.simulator.go(WAIT_TIME)
routes = leader.get_routes()
+ self.assertEqual([route.split(' ')[0] == '::/0' for route in routes].count(True), 0)
self.check_num_of_routes(routes, num - 1, 0, 1)
# Publish the same prefix on leader as an on-mesh prefix. Make
diff --git a/tests/scripts/thread-cert/test_srp_server_reboot_port.py b/tests/scripts/thread-cert/test_srp_server_reboot_port.py
index d78dc11..b38bcfe 100755
--- a/tests/scripts/thread-cert/test_srp_server_reboot_port.py
+++ b/tests/scripts/thread-cert/test_srp_server_reboot_port.py
@@ -94,14 +94,14 @@
#
# 2. Reboot the server without any service registered. The server should
- # listen to the same port after the reboot.
+ # switch to a new port after re-enabling.
#
old_port = server.get_srp_server_port()
server.srp_server_set_enabled(False)
self.simulator.go(5)
server.srp_server_set_enabled(True)
self.simulator.go(5)
- self.assertEqual(old_port, server.get_srp_server_port())
+ self.assertNotEqual(old_port, server.get_srp_server_port())
#
# 3. Register a service
diff --git a/tests/scripts/thread-cert/v1_2_router_5_1_1.py b/tests/scripts/thread-cert/v1_2_router_5_1_1.py
index 6f8b01c..146b2b5 100755
--- a/tests/scripts/thread-cert/v1_2_router_5_1_1.py
+++ b/tests/scripts/thread-cert/v1_2_router_5_1_1.py
@@ -79,7 +79,7 @@
msg.assertMleMessageContainsTlv(mle.Challenge)
msg.assertMleMessageContainsTlv(mle.ScanMask)
msg.assertMleMessageContainsTlv(mle.Version)
- assert msg.get_mle_message_tlv(mle.Version).version >= config.THREAD_VERSION_1_2
+ self.assertGreaterEqual(msg.get_mle_message_tlv(mle.Version).version, config.THREAD_VERSION_1_2)
scan_mask_tlv = msg.get_mle_message_tlv(mle.ScanMask)
self.assertEqual(1, scan_mask_tlv.router)
@@ -97,7 +97,7 @@
msg.assertMleMessageContainsTlv(mle.LinkMargin)
msg.assertMleMessageContainsTlv(mle.Connectivity)
msg.assertMleMessageContainsTlv(mle.Version)
- assert msg.get_mle_message_tlv(mle.Version).version >= config.THREAD_VERSION_1_2
+ self.assertGreaterEqual(msg.get_mle_message_tlv(mle.Version).version, config.THREAD_VERSION_1_2)
# 4 - Router_1 receives the MLE Parent Response and sends a Child ID Request
msg = router_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
@@ -110,7 +110,7 @@
msg.assertMleMessageContainsTlv(mle.Version)
msg.assertMleMessageContainsTlv(mle.TlvRequest)
msg.assertMleMessageDoesNotContainTlv(mle.AddressRegistration)
- assert msg.get_mle_message_tlv(mle.Version).version >= config.THREAD_VERSION_1_2
+ self.assertGreaterEqual(msg.get_mle_message_tlv(mle.Version).version, config.THREAD_VERSION_1_2)
# 5 - Leader responds with a Child ID Response
msg = leader_messages.next_mle_message(mle.CommandType.CHILD_ID_RESPONSE)
diff --git a/tests/scripts/thread-cert/v1_2_test_backbone_router_service.py b/tests/scripts/thread-cert/v1_2_test_backbone_router_service.py
index 36d5264..888a6d9 100755
--- a/tests/scripts/thread-cert/v1_2_test_backbone_router_service.py
+++ b/tests/scripts/thread-cert/v1_2_test_backbone_router_service.py
@@ -128,7 +128,7 @@
WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(), 'Primary')
- assert self.nodes[BBR_1].get_backbone_router()['seqno'] == 2
+ self.assertEqual(self.nodes[BBR_1].get_backbone_router()['seqno'], 2)
# 3) Reset BBR_1 and bring it back after its original router id is released
# 200s (100s MaxNeighborAge + 90s InfiniteCost + 10s redundance)
@@ -186,7 +186,7 @@
# Check no SRV_DATA.ntf.
messages = self.simulator.get_messages_sent_by(BBR_2)
msg = messages.next_coap_message('0.02', '/a/sd', False)
- assert (msg is None), "Error: %d sent unexpected SRV_DATA.ntf when there is PBbr already"
+ self.assertIsNone(msg)
# Flush relative message queue.
self.flush_nodes([BBR_1])
@@ -203,7 +203,7 @@
messages.next_coap_message('0.02', '/a/sd', True)
self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(), 'Secondary')
# Verify Sequence number increases when become Secondary from Primary.
- assert self.nodes[BBR_1].get_backbone_router()['seqno'] == (BBR_1_SEQNO + 1)
+ self.assertEqual(self.nodes[BBR_1].get_backbone_router()['seqno'], BBR_1_SEQNO + 1)
# 4a) Check communication via DUA.
bbr2_dua = self.nodes[BBR_2].get_addr(config.DOMAIN_PREFIX)
@@ -238,7 +238,7 @@
# 6a) Check the uniqueness of DUA by comparing the one in above 4a).
bbr2_dua2 = self.nodes[BBR_2].get_addr(config.DOMAIN_PREFIX)
- assert bbr2_dua == bbr2_dua2, 'Error: Unexpected different DUA ({} v.s. {})'.format(bbr2_dua, bbr2_dua2)
+ self.assertEqual(bbr2_dua, bbr2_dua2)
# 6b) Check communication via DUA
self.assertTrue(self.nodes[BBR_1].ping(bbr2_dua))
diff --git a/tests/scripts/thread-cert/v1_2_test_domain_unicast_address.py b/tests/scripts/thread-cert/v1_2_test_domain_unicast_address.py
index 7a98680..81c9054 100755
--- a/tests/scripts/thread-cert/v1_2_test_domain_unicast_address.py
+++ b/tests/scripts/thread-cert/v1_2_test_domain_unicast_address.py
@@ -242,10 +242,8 @@
WAIT_TIME = WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
- assert ipaddress.ip_address(dua) == ipaddress.ip_address(
- med_1_2_dua), 'Error: Expected SLAAC DUA not generated'
- assert ipaddress.ip_address(med_1_2_dua) == ipaddress.ip_address(
- dua), 'Error: Expected same SLAAC DUA not generated'
+ self.assertEqual(ipaddress.ip_address(dua), ipaddress.ip_address(med_1_2_dua))
+ self.assertEqual(ipaddress.ip_address(med_1_2_dua), ipaddress.ip_address(dua))
self.__check_dua_registration(MED_1_2, med_1_2_dua_iid, domain_prefix_cid)
@@ -269,8 +267,7 @@
self.simulator.go(WAIT_TIME)
dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
assert dua, 'Error: Expected DUA not found'
- assert ipaddress.ip_address(med_1_2_dua) == ipaddress.ip_address(
- dua), 'Error: Expected same SLAAC DUA not generated'
+ self.assertEqual(ipaddress.ip_address(med_1_2_dua), ipaddress.ip_address(dua))
self.__check_dua_registration(MED_1_2, med_1_2_dua_iid, domain_prefix_cid)
@@ -299,8 +296,7 @@
self.simulator.go(WAIT_TIME)
dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
assert dua, 'Error: Expected DUA not found'
- assert ipaddress.ip_address(med_1_2_dua) == ipaddress.ip_address(
- dua), 'Error: Expected same SLAAC DUA not generated'
+ self.assertEqual(ipaddress.ip_address(med_1_2_dua), ipaddress.ip_address(dua))
self.__check_dua_registration(MED_1_2, med_1_2_dua_iid, domain_prefix_cid)
diff --git a/tests/scripts/thread-cert/v1_2_test_domain_unicast_address_registration.py b/tests/scripts/thread-cert/v1_2_test_domain_unicast_address_registration.py
index 4053fec..76772ab 100755
--- a/tests/scripts/thread-cert/v1_2_test_domain_unicast_address_registration.py
+++ b/tests/scripts/thread-cert/v1_2_test_domain_unicast_address_registration.py
@@ -276,7 +276,7 @@
dua2 = self.nodes[ROUTER_1_2].get_addr(config.DOMAIN_PREFIX)
assert dua2, 'Error: Expected DUA ({}) not found'.format(dua2)
- assert dua2 != dua, 'Error: Expected Different DUA not found, same DUA {}'.format(dua2)
+ self.assertNotEqual(dua2, dua)
# e) (repeated) Configure BBR_1 to respond with per remaining error status:
# - increase BBR seqno to trigger reregistration
diff --git a/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py b/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py
index 1887536..ac974de 100755
--- a/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py
+++ b/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py
@@ -704,7 +704,7 @@
def __check_renewing(self, id, parent_id, addr, expect_mlr_req=True, expect_mlr_req_proxied=False):
"""Check if MLR works that a node can renew it's registered MAs"""
- assert self.pbbr_id == BBR_1
+ self.assertEqual(self.pbbr_id, BBR_1)
self.flush_all()
self.simulator.go(MLR_TIMEOUT + WAIT_REDUNDANCE)
@@ -748,7 +748,7 @@
def __check_rereg_pbbr_change(self, id, parent_id, addr, expect_mlr_req=True, expect_mlr_req_proxied=False):
"""Check if MLR works that a node can do MLR reregistration when PBBR changes"""
# Make BBR_2 to be Primary and expect MLR.req within REREG_DELAY
- assert self.pbbr_id == BBR_1
+ self.assertEqual(self.pbbr_id, BBR_1)
self.flush_all()
self.nodes[BBR_1].disable_backbone_router()
diff --git a/tests/scripts/thread-cert/v1_2_test_parent_selection.py b/tests/scripts/thread-cert/v1_2_test_parent_selection.py
index 7b55b91..caf979f 100755
--- a/tests/scripts/thread-cert/v1_2_test_parent_selection.py
+++ b/tests/scripts/thread-cert/v1_2_test_parent_selection.py
@@ -139,8 +139,8 @@
assert (parent_cmp), "Error: Expected parent response not found"
# Known that link margin for link quality 3 is 80 and link quality 2 is 15
- assert ((parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin -
- parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin) > 20)
+ self.assertGreater((parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin -
+ parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin), 20)
# Check Child Id Request
messages = self.simulator.get_messages_sent_by(REED_1_2)
@@ -174,8 +174,9 @@
parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
assert (parent_cmp), "Error: Expected parent response not found"
- assert (parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin == parent_cmp.get_mle_message_tlv(
- mle.LinkMargin).link_margin)
+ self.assertEqual(
+ parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
+ parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
# Check Child Id Request
messages = self.simulator.get_messages_sent_by(ROUTER_1_2)
@@ -204,11 +205,13 @@
parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
assert (parent_cmp), "Error: Expected parent response not found"
- assert (parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin == parent_cmp.get_mle_message_tlv(
- mle.LinkMargin).link_margin)
+ self.assertEqual(
+ parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
+ parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
- assert (parent_prefer.get_mle_message_tlv(mle.Connectivity).pp > parent_cmp.get_mle_message_tlv(
- mle.Connectivity).pp)
+ self.assertGreater(
+ parent_prefer.get_mle_message_tlv(mle.Connectivity).pp,
+ parent_cmp.get_mle_message_tlv(mle.Connectivity).pp)
# Check Child Id Request
messages = self.simulator.get_messages_sent_by(REED_1_1)
@@ -239,12 +242,15 @@
parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
assert (parent_cmp), "Error: Expected parent response not found"
- assert (parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin == parent_cmp.get_mle_message_tlv(
- mle.LinkMargin).link_margin)
- assert (parent_prefer.get_mle_message_tlv(mle.Connectivity).pp == parent_cmp.get_mle_message_tlv(
- mle.Connectivity).pp)
- assert (parent_prefer.get_mle_message_tlv(mle.Connectivity).link_quality_3 > parent_cmp.get_mle_message_tlv(
- mle.Connectivity).link_quality_3)
+ self.assertEqual(
+ parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
+ parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
+ self.assertEqual(
+ parent_prefer.get_mle_message_tlv(mle.Connectivity).pp,
+ parent_cmp.get_mle_message_tlv(mle.Connectivity).pp)
+ self.assertGreater(
+ parent_prefer.get_mle_message_tlv(mle.Connectivity).link_quality_3,
+ parent_cmp.get_mle_message_tlv(mle.Connectivity).link_quality_3)
# Check Child Id Request
messages = self.simulator.get_messages_sent_by(MED_1_1)
@@ -272,14 +278,18 @@
parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
assert (parent_cmp), "Error: Expected parent response not found"
- assert (parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin == parent_cmp.get_mle_message_tlv(
- mle.LinkMargin).link_margin)
- assert (parent_prefer.get_mle_message_tlv(mle.Connectivity).pp == parent_cmp.get_mle_message_tlv(
- mle.Connectivity).pp)
- assert (parent_prefer.get_mle_message_tlv(mle.Connectivity).link_quality_3 == parent_cmp.get_mle_message_tlv(
- mle.Connectivity).link_quality_3)
- assert (parent_prefer.get_mle_message_tlv(mle.Version).version > parent_cmp.get_mle_message_tlv(
- mle.Version).version)
+ self.assertEqual(
+ parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
+ parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
+ self.assertEqual(
+ parent_prefer.get_mle_message_tlv(mle.Connectivity).pp,
+ parent_cmp.get_mle_message_tlv(mle.Connectivity).pp)
+ self.assertEqual(
+ parent_prefer.get_mle_message_tlv(mle.Connectivity).link_quality_3,
+ parent_cmp.get_mle_message_tlv(mle.Connectivity).link_quality_3)
+ self.assertGreater(
+ parent_prefer.get_mle_message_tlv(mle.Version).version,
+ parent_cmp.get_mle_message_tlv(mle.Version).version)
# Check Child Id Request
messages = self.simulator.get_messages_sent_by(MED_1_2)
diff --git a/tests/toranj/build.sh b/tests/toranj/build.sh
index 0c9a698..4b91cd4 100755
--- a/tests/toranj/build.sh
+++ b/tests/toranj/build.sh
@@ -113,8 +113,9 @@
echo "==================================================================================================="
cd "${top_builddir}" || die "cd failed"
cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
- -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+ -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
-DOT_OPERATIONAL_DATASET_AUTO_INIT=ON -DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
+ -DOT_BORDER_ROUTING=OFF \
-DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
"${top_srcdir}" || die
ninja || die
@@ -126,8 +127,9 @@
echo "==================================================================================================="
cd "${top_builddir}" || die "cd failed"
cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
- -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+ -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
-DOT_15_4=ON -DOT_TREL=OFF -DOT_OPERATIONAL_DATASET_AUTO_INIT=ON \
+ -DOT_BORDER_ROUTING=OFF \
-DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
-DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
"${top_srcdir}" || die
@@ -141,8 +143,9 @@
echo "==================================================================================================="
cd "${top_builddir}" || die "cd failed"
cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
- -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+ -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
-DOT_15_4=OFF -DOT_TREL=ON -DOT_OPERATIONAL_DATASET_AUTO_INIT=ON \
+ -DOT_BORDER_ROUTING=OFF \
-DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
-DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
"${top_srcdir}" || die
@@ -156,8 +159,9 @@
echo "==================================================================================================="
cd "${top_builddir}" || die "cd failed"
cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
- -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+ -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=OFF -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
-DOT_15_4=ON -DOT_TREL=ON -DOT_OPERATIONAL_DATASET_AUTO_INIT=ON \
+ -DOT_BORDER_ROUTING=OFF \
-DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
-DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
"${top_srcdir}" || die
@@ -171,7 +175,7 @@
echo "==================================================================================================="
cd "${top_builddir}" || die "cd failed"
cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
- -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
+ -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
-DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
-DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
"${top_srcdir}" || die
@@ -184,7 +188,7 @@
echo "==================================================================================================="
cd "${top_builddir}" || die "cd failed"
cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
- -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
+ -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
-DOT_15_4=ON -DOT_TREL=OFF \
-DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
-DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
@@ -199,7 +203,7 @@
echo "==================================================================================================="
cd "${top_builddir}" || die "cd failed"
cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
- -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
+ -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
-DOT_15_4=OFF -DOT_TREL=ON \
-DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
-DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
@@ -214,7 +218,7 @@
echo "==================================================================================================="
cd "${top_builddir}" || die "cd failed"
cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
- -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
+ -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \
-DOT_15_4=ON -DOT_TREL=ON \
-DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
-DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
@@ -229,7 +233,7 @@
echo "===================================================================================================="
cd "${top_builddir}" || die "cd failed"
cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
- -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=OFF -DOT_APP_NCP=OFF -DOT_APP_RCP=ON \
+ -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=OFF -DOT_APP_NCP=OFF -DOT_APP_RCP=ON \
-DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
-DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
"${top_srcdir}" || die
@@ -242,7 +246,7 @@
echo "===================================================================================================="
cd "${top_builddir}" || die "cd failed"
cmake -GNinja -DOT_PLATFORM=posix -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
- -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+ -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
-DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
-DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-posix.h \
"${top_srcdir}" || die
@@ -255,7 +259,7 @@
echo "===================================================================================================="
cd "${top_builddir}" || die "cd failed"
cmake -GNinja -DOT_PLATFORM=posix -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
- -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+ -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
-DOT_15_4=ON -DOT_TREL=OFF \
-DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
-DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-posix.h \
@@ -269,7 +273,7 @@
echo "===================================================================================================="
cd "${top_builddir}" || die "cd failed"
cmake -GNinja -DOT_PLATFORM=posix -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
- -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+ -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
-DOT_15_4=OFF -DOT_TREL=ON \
-DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
-DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-posix.h \
@@ -283,7 +287,7 @@
echo "===================================================================================================="
cd "${top_builddir}" || die "cd failed"
cmake -GNinja -DOT_PLATFORM=posix -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
- -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
+ -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=OFF \
-DOT_15_4=ON -DOT_TREL=ON \
-DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
-DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-posix.h \
@@ -297,7 +301,7 @@
echo "===================================================================================================="
cd "${top_builddir}" || die "cd failed"
cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \
- -DOT_THREAD_VERSION=1.3.1 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=ON \
+ -DOT_THREAD_VERSION=1.4 -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=ON \
-DOT_PLATFORM_KEY_REF=${ot_plat_key_ref} \
-DOT_PROJECT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \
"${top_srcdir}" || die
diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py
index 385d8e5..8f278f4 100644
--- a/tests/toranj/cli/cli.py
+++ b/tests/toranj/cli/cli.py
@@ -223,6 +223,17 @@
def set_channel(self, channel):
self._cli_no_output('channel', channel)
+ def get_csl_config(self):
+ outputs = self.cli('csl')
+ result = {}
+ for line in outputs:
+ fields = line.split(':')
+ result[fields[0].strip()] = fields[1].strip()
+ return result
+
+ def set_csl_period(self, period):
+ self._cli_no_output('csl period', period)
+
def get_ext_addr(self):
return self._cli_single_output('extaddr')
@@ -387,19 +398,23 @@
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# netdata
- def get_netdata(self):
- outputs = self.cli('netdata show')
+ def get_netdata(self, rloc16=None):
+ outputs = self.cli('netdata show', rloc16)
outputs = [line.strip() for line in outputs]
routes_index = outputs.index('Routes:')
services_index = outputs.index('Services:')
- contexts_index = outputs.index('Contexts:')
- commissioning_index = outputs.index('Commissioning:')
+ if rloc16 is None:
+ contexts_index = outputs.index('Contexts:')
+ commissioning_index = outputs.index('Commissioning:')
result = {}
result['prefixes'] = outputs[1:routes_index]
result['routes'] = outputs[routes_index + 1:services_index]
- result['services'] = outputs[services_index + 1:contexts_index]
- result['contexts'] = outputs[contexts_index + 1:commissioning_index]
- result['commissioning'] = outputs[commissioning_index + 1:]
+ if rloc16 is None:
+ result['services'] = outputs[services_index + 1:contexts_index]
+ result['contexts'] = outputs[contexts_index + 1:commissioning_index]
+ result['commissioning'] = outputs[commissioning_index + 1:]
+ else:
+ result['services'] = outputs[services_index + 1:]
return result
@@ -472,6 +487,24 @@
return self._cli_single_output('mleadvimax')
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # Border Agent
+
+ def ba_get_state(self):
+ return self._cli_single_output('ba state')
+
+ def ba_get_port(self):
+ return self._cli_single_output('ba port')
+
+ def ba_is_ephemeral_key_active(self):
+ return self._cli_single_output('ba ephemeralkey')
+
+ def ba_set_ephemeral_key(self, keystring, timeout=None, port=None):
+ self._cli_no_output('ba ephemeralkey set', keystring, timeout, port)
+
+ def ba_clear_ephemeral_key(self):
+ self._cli_no_output('ba ephemeralkey clear')
+
+ #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# UDP
def udp_open(self):
@@ -624,6 +657,12 @@
def srp_server_disable(self):
self._cli_no_output('srp server disable')
+ def srp_server_auto_enable(self):
+ self._cli_no_output('srp server auto enable')
+
+ def srp_server_auto_disable(self):
+ self._cli_no_output('srp server auto disable')
+
def srp_server_set_lease(self, min_lease, max_lease, min_key_lease, max_key_lease):
self._cli_no_output('srp server lease', min_lease, max_lease, min_key_lease, max_key_lease)
@@ -742,6 +781,18 @@
def br_get_state(self):
return self._cli_single_output('br state')
+ def br_get_favored_omrprefix(self):
+ return self._cli_single_output('br omrprefix favored')
+
+ def br_get_local_omrprefix(self):
+ return self._cli_single_output('br omrprefix local')
+
+ def br_get_favored_onlinkprefix(self):
+ return self._cli_single_output('br onlinkprefix favored')
+
+ def br_get_local_onlinkprefix(self):
+ return self._cli_single_output('br onlinkprefix local')
+
def br_get_routeprf(self):
return self._cli_single_output('br routeprf')
@@ -751,6 +802,9 @@
def br_clear_routeprf(self):
self._cli_no_output('br routeprf clear')
+ def br_get_routers(self):
+ return self.cli('br routers')
+
# ------------------------------------------------------------------------------------------------------------------
# Helper methods
@@ -932,7 +986,8 @@
except VerifyError as e:
if time.time() - start_time > wait_time:
print('Took too long to pass the condition ({}>{} sec)'.format(time.time() - start_time, wait_time))
- print(e.message)
+ if hasattr(e, 'message'):
+ print(e.message)
raise e
except BaseException:
raise
diff --git a/tests/toranj/cli/test-011-network-data-timeout.py b/tests/toranj/cli/test-011-network-data-timeout.py
index a3a4086..bf52487 100755
--- a/tests/toranj/cli/test-011-network-data-timeout.py
+++ b/tests/toranj/cli/test-011-network-data-timeout.py
@@ -89,11 +89,6 @@
# -----------------------------------------------------------------------------------------------------------------------
# Test Implementation
-common_prefix = 'fd00:cafe::'
-prefix1 = 'fd00:1::'
-prefix2 = 'fd00:2::'
-prefix3 = 'fd00:3::'
-
# Each node adds its own prefix.
r1.add_prefix('fd00:1::/64', 'paros', 'med')
r2.add_prefix('fd00:2::/64', 'paros', 'med')
@@ -104,6 +99,9 @@
r2.add_prefix('fd00:abba::/64', 'paros', 'med')
c2.add_prefix('fd00:abba::/64', 'paros', 'low')
+r1.add_route('fd00:cafe::/64', 's', 'med')
+r2.add_route('fd00:cafe::/64', 's', 'med')
+
r1.register_netdata()
r2.register_netdata()
c2.register_netdata()
@@ -112,12 +110,19 @@
def check_netdata_on_all_nodes():
for node in nodes:
netdata = node.get_netdata()
- prefixes = netdata['prefixes']
- verify(len(prefixes) == 6)
+ verify(len(netdata['prefixes']) == 6)
+ verify(len(netdata['routes']) == 2)
verify_within(check_netdata_on_all_nodes, 10)
+# Check netdata filtering for r1 entries only.
+
+r1_rloc = int(r1.get_rloc16(), 16)
+netdata = r1.get_netdata(r1_rloc)
+verify(len(netdata['prefixes']) == 2)
+verify(len(netdata['routes']) == 1)
+
# Remove `r2`. This should trigger all the prefixes added by it or its
# child to timeout and be removed.
@@ -127,8 +132,11 @@
def check_netdata_on_r1():
netdata = r1.get_netdata()
- prefixes = netdata['prefixes']
- verify(len(prefixes) == 2)
+ verify(len(netdata['prefixes']) == 2)
+ verify(len(netdata['routes']) == 1)
+ netdata = r1.get_netdata(r1_rloc)
+ verify(len(netdata['prefixes']) == 2)
+ verify(len(netdata['routes']) == 1)
verify_within(check_netdata_on_r1, 120)
diff --git a/tests/toranj/cli/test-020-net-diag-vendor-info.py b/tests/toranj/cli/test-020-net-diag-vendor-info.py
index a8bb7da..463854a 100755
--- a/tests/toranj/cli/test-020-net-diag-vendor-info.py
+++ b/tests/toranj/cli/test-020-net-diag-vendor-info.py
@@ -76,12 +76,12 @@
r1.set_vendor_name('nest')
r1.set_vendor_model('marble')
-r1.set_vendor_sw_version('ot-1.3.1')
+r1.set_vendor_sw_version('ot-1.4')
r1.set_vendor_app_url('https://example.com/vendor-app')
verify(r1.get_vendor_name() == 'nest')
verify(r1.get_vendor_model() == 'marble')
-verify(r1.get_vendor_sw_version() == 'ot-1.3.1')
+verify(r1.get_vendor_sw_version() == 'ot-1.4')
verify(r1.get_vendor_app_url() == 'https://example.com/vendor-app')
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/tests/toranj/cli/test-028-border-agent-ephemeral-key.py b/tests/toranj/cli/test-028-border-agent-ephemeral-key.py
new file mode 100755
index 0000000..47a02ee
--- /dev/null
+++ b/tests/toranj/cli/test-028-border-agent-ephemeral-key.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2023, The OpenThread Authors.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the
+# names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+from cli import verify
+from cli import verify_within
+import cli
+import time
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test description:
+#
+# Validate changes to `IntervalMax` for MLE Advertisement Trickle Timer based on number of
+# router neighbors of the device.
+#
+
+test_name = __file__[:-3] if __file__.endswith('.py') else __file__
+print('-' * 120)
+print('Starting \'{}\''.format(test_name))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Creating `cli.Node` instances
+
+speedup = 20
+cli.Node.set_time_speedup_factor(speedup)
+
+leader = cli.Node()
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test Implementation
+
+leader.form('ba-ephemeral')
+
+verify(leader.get_state() == 'leader')
+
+verify(leader.ba_is_ephemeral_key_active() == 'inactive')
+
+port = int(leader.ba_get_port())
+
+leader.ba_set_ephemeral_key('password', 10000, 1234)
+
+time.sleep(0.1)
+
+verify(leader.ba_is_ephemeral_key_active() == 'active')
+verify(int(leader.ba_get_port()) == 1234)
+
+leader.ba_set_ephemeral_key('password2', 200, 45678)
+
+time.sleep(0.100 / speedup)
+verify(leader.ba_is_ephemeral_key_active() == 'active')
+verify(int(leader.ba_get_port()) == 45678)
+
+time.sleep(0.150 / speedup)
+verify(leader.ba_is_ephemeral_key_active() == 'inactive')
+verify(int(leader.ba_get_port()) == port)
+
+leader.ba_set_ephemeral_key('newkey')
+verify(leader.ba_is_ephemeral_key_active() == 'active')
+
+time.sleep(0.1)
+verify(leader.ba_is_ephemeral_key_active() == 'active')
+
+leader.ba_clear_ephemeral_key()
+verify(leader.ba_is_ephemeral_key_active() == 'inactive')
+verify(int(leader.ba_get_port()) == port)
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test finished
+
+cli.Node.finalize_all_nodes()
+
+print('\'{}\' passed.'.format(test_name))
diff --git a/tests/toranj/cli/test-400-srp-client-server.py b/tests/toranj/cli/test-400-srp-client-server.py
index e65d6ff..0e3edb2 100755
--- a/tests/toranj/cli/test-400-srp-client-server.py
+++ b/tests/toranj/cli/test-400-srp-client-server.py
@@ -121,6 +121,22 @@
verify(service['host'] == 'host')
verify(service['addresses'] == ['fd00:0:0:0:0:0:0:cafe'])
+# Check the client address is added in EID cache table (snoop).
+
+cache_table = server.get_eidcache()
+client_rloc = int(client.get_rloc16(), 16)
+found_entry = False
+
+for entry in cache_table:
+ fields = entry.strip().split(' ')
+ if (fields[0] == 'fd00:0:0:0:0:0:0:cafe'):
+ verify(int(fields[1], 16) == client_rloc)
+ verify(fields[2] == 'snoop')
+ found_entry = True
+ break
+
+verify(found_entry)
+
# -----------------------------------------------------------------------------------------------------------------------
# Test finished
diff --git a/tests/toranj/cli/test-401-srp-server-address-cache-snoop.py b/tests/toranj/cli/test-401-srp-server-address-cache-snoop.py
new file mode 100755
index 0000000..2462d0a
--- /dev/null
+++ b/tests/toranj/cli/test-401-srp-server-address-cache-snoop.py
@@ -0,0 +1,213 @@
+#!/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:
+#
+# Validate registered host addresses are properly added in address cache table
+# on SRP server.
+#
+# r1 (leader) ----- r2 ------ r3
+# / | \
+# / | \
+# fed1 sed1 sed2
+#
+
+test_name = __file__[:-3] if __file__.endswith('.py') else __file__
+print('-' * 120)
+print('Starting \'{}\''.format(test_name))
+
+#-----------------------------------------------------------------------------------------------------------------------
+# Creating `cli.Nodes` instances
+
+speedup = 10
+cli.Node.set_time_speedup_factor(speedup)
+
+r1 = cli.Node()
+r2 = cli.Node()
+r3 = cli.Node()
+fed1 = cli.Node()
+sed1 = cli.Node()
+sed2 = cli.Node()
+
+WAIT_TIME = 5
+
+#-----------------------------------------------------------------------------------------------------------------------
+# Form topology
+
+r1.allowlist_node(r2)
+r1.allowlist_node(fed1)
+r1.allowlist_node(sed1)
+
+r2.allowlist_node(r1)
+r2.allowlist_node(r3)
+r2.allowlist_node(sed2)
+
+r3.allowlist_node(r2)
+r3.allowlist_node(sed2)
+
+fed1.allowlist_node(r1)
+sed1.allowlist_node(r1)
+
+sed2.allowlist_node(r3)
+
+r1.form('srp-snoop')
+r2.join(r1)
+r3.join(r2)
+fed1.join(r1, cli.JOIN_TYPE_REED)
+sed1.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE)
+sed2.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE)
+sed1.set_pollperiod(500)
+sed2.set_pollperiod(500)
+
+verify(r1.get_state() == 'leader')
+verify(r2.get_state() == 'router')
+verify(r2.get_state() == 'router')
+verify(sed1.get_state() == 'child')
+verify(sed2.get_state() == 'child')
+verify(fed1.get_state() == 'child')
+
+r2_rloc = int(r2.get_rloc16(), 16)
+r3_rloc = int(r3.get_rloc16(), 16)
+fed1_rloc = int(fed1.get_rloc16(), 16)
+
+# Start server and client and register single service
+r1.srp_server_enable()
+
+r2.srp_client_enable_auto_start_mode()
+r2.srp_client_set_host_name('r2')
+r2.srp_client_set_host_address('fd00::2')
+r2.srp_client_add_service('srv2', '_test._udp', 222, 0, 0)
+
+
+def check_server_has_host(num_hosts):
+ verify(len(r1.srp_server_get_hosts()) >= num_hosts)
+
+
+verify_within(check_server_has_host, WAIT_TIME, arg=1)
+
+cache_table = r1.get_eidcache()
+
+for entry in cache_table:
+ fields = entry.strip().split(' ')
+ if (fields[0] == 'fd00:0:0:0:0:0:0:2'):
+ verify(int(fields[1], 16) == r2_rloc)
+ verify(fields[2] == 'snoop')
+ break
+else:
+ verify(False) # did not find cache entry
+
+# Register from r3 which one hop away from r1 server.
+
+r3.srp_client_enable_auto_start_mode()
+r3.srp_client_set_host_name('r3')
+r3.srp_client_set_host_address('fd00::3')
+r3.srp_client_add_service('srv3', '_test._udp', 333, 0, 0)
+
+verify_within(check_server_has_host, WAIT_TIME, arg=2)
+
+cache_table = r1.get_eidcache()
+
+for entry in cache_table:
+ fields = entry.strip().split(' ')
+ if (fields[0] == 'fd00:0:0:0:0:0:0:3'):
+ verify(int(fields[1], 16) == r3_rloc)
+ verify(fields[2] == 'snoop')
+ break
+else:
+ verify(False) # did not find cache entry
+
+# Register from sed2 which child of r3. The cache table should
+# use the `r3` as the parent of sed2.
+
+sed2.srp_client_enable_auto_start_mode()
+sed2.srp_client_set_host_name('sed2')
+sed2.srp_client_set_host_address('fd00::1:3')
+sed2.srp_client_add_service('srv4', '_test._udp', 333, 0, 0)
+
+verify_within(check_server_has_host, WAIT_TIME, arg=3)
+
+cache_table = r1.get_eidcache()
+
+for entry in cache_table:
+ fields = entry.strip().split(' ')
+ if (fields[0] == 'fd00:0:0:0:0:0:1:3'):
+ verify(int(fields[1], 16) == r3_rloc)
+ verify(fields[2] == 'snoop')
+ break
+else:
+ verify(False) # did not find cache entry
+
+# Register from fed1 which child of server (r1) itself. The cache table should
+# be properly updated
+
+fed1.srp_client_enable_auto_start_mode()
+fed1.srp_client_set_host_name('fed1')
+fed1.srp_client_set_host_address('fd00::2:3')
+fed1.srp_client_add_service('srv5', '_test._udp', 555, 0, 0)
+
+verify_within(check_server_has_host, WAIT_TIME, arg=4)
+
+cache_table = r1.get_eidcache()
+
+for entry in cache_table:
+ fields = entry.strip().split(' ')
+ if (fields[0] == 'fd00:0:0:0:0:0:2:3'):
+ verify(int(fields[1], 16) == fed1_rloc)
+ verify(fields[2] == 'snoop')
+ break
+else:
+ verify(False) # did not find cache entry
+
+# Register from sed1 which is a sleepy child of server (r1).
+# The cache table should not be updated for sleepy child.
+
+sed1.srp_client_enable_auto_start_mode()
+sed1.srp_client_set_host_name('sed1')
+sed1.srp_client_set_host_address('fd00::3:4')
+sed1.srp_client_add_service('srv5', '_test._udp', 555, 0, 0)
+
+verify_within(check_server_has_host, WAIT_TIME, arg=4)
+
+cache_table = r1.get_eidcache()
+
+for entry in cache_table:
+ fields = entry.strip().split(' ')
+ verify(fields[0] != 'fd00:0:0:0:0:0:3:4')
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test finished
+
+cli.Node.finalize_all_nodes()
+
+print('\'{}\' passed.'.format(test_name))
diff --git a/tests/toranj/cli/test-500-two-brs-two-networks.py b/tests/toranj/cli/test-500-two-brs-two-networks.py
new file mode 100755
index 0000000..12c61a7
--- /dev/null
+++ b/tests/toranj/cli/test-500-two-brs-two-networks.py
@@ -0,0 +1,111 @@
+#!/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:
+#
+# Two BRs on two different Thread networks.
+
+test_name = __file__[:-3] if __file__.endswith('.py') else __file__
+print('-' * 120)
+print('Starting \'{}\''.format(test_name))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Creating `cli.Nodes` instances
+
+speedup = 60
+cli.Node.set_time_speedup_factor(speedup)
+
+br1 = cli.Node()
+br2 = cli.Node()
+
+WAIT_TIME = 5
+IF_INDEX = 1
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test implementation
+
+# Start first BR with its own Thread network
+
+br1.form('net1')
+verify(br1.get_state() == 'leader')
+
+br1.br_init(IF_INDEX, 1)
+br1.br_enable()
+
+time.sleep(1)
+verify(br1.br_get_state() == 'running')
+
+br1_local_omr = br1.br_get_local_omrprefix()
+br1_favored_omr = br1.br_get_favored_omrprefix().split()[0]
+verify(br1_local_omr == br1_favored_omr)
+
+br1_local_onlink = br1.br_get_local_onlinkprefix()
+br1_favored_onlink = br1.br_get_favored_onlinkprefix().split()[0]
+verify(br1_local_onlink == br1_favored_onlink)
+
+# Start second BR with its own Thread network.
+
+br2.form('net2')
+verify(br2.get_state() == 'leader')
+
+br2.br_init(IF_INDEX, 1)
+br2.br_enable()
+
+time.sleep(1)
+verify(br2.br_get_state() == 'running')
+
+br2_local_omr = br2.br_get_local_omrprefix()
+br2_favored_omr = br2.br_get_favored_omrprefix().split()[0]
+verify(br2_local_omr == br2_favored_omr)
+
+br2_local_onlink = br2.br_get_local_onlinkprefix()
+br2_favored_onlink = br2.br_get_favored_onlinkprefix().split()[0]
+verify(br2_local_onlink != br2_favored_onlink)
+
+# BR2 should see and favor the on-link prefix already advertised by BR1.
+
+verify(br1_favored_onlink == br2_favored_onlink)
+
+br1_routers = br1.br_get_routers()
+br2_routers = br2.br_get_routers()
+
+verify(len(br1_routers) > 0)
+verify(len(br2_routers) > 0)
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test finished
+
+cli.Node.finalize_all_nodes()
+
+print('\'{}\' passed.'.format(test_name))
diff --git a/tests/toranj/cli/test-501-multi-br-failure-recovery.py b/tests/toranj/cli/test-501-multi-br-failure-recovery.py
new file mode 100755
index 0000000..ecf41b9
--- /dev/null
+++ b/tests/toranj/cli/test-501-multi-br-failure-recovery.py
@@ -0,0 +1,210 @@
+#!/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:
+#
+# Network with two BRs, none of them acting as leader. Removing BR1 ensuring BR2 taking over.
+#
+# ________________
+# / \
+# br1 --- leader --- br2
+# / / \ \
+# c1 c2 c3 c4
+#
+
+test_name = __file__[:-3] if __file__.endswith('.py') else __file__
+print('-' * 120)
+print('Starting \'{}\''.format(test_name))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Creating `cli.Nodes` instances
+
+speedup = 60
+cli.Node.set_time_speedup_factor(speedup)
+
+leader = cli.Node()
+br1 = cli.Node()
+br2 = cli.Node()
+c1 = cli.Node()
+c2 = cli.Node()
+c3 = cli.Node()
+c4 = cli.Node()
+
+IF_INDEX = 1
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Form topology
+
+leader.allowlist_node(br1)
+leader.allowlist_node(br2)
+leader.allowlist_node(c2)
+leader.allowlist_node(c3)
+
+br1.allowlist_node(leader)
+br1.allowlist_node(br2)
+br1.allowlist_node(c1)
+
+br2.allowlist_node(leader)
+br2.allowlist_node(br1)
+br2.allowlist_node(c4)
+
+c1.allowlist_node(br1)
+
+c2.allowlist_node(leader)
+c3.allowlist_node(leader)
+
+c4.allowlist_node(br2)
+
+leader.form("multi-br")
+br1.join(leader)
+br2.join(leader)
+c1.join(leader, cli.JOIN_TYPE_END_DEVICE)
+c2.join(leader, cli.JOIN_TYPE_END_DEVICE)
+c3.join(leader, cli.JOIN_TYPE_END_DEVICE)
+c4.join(leader, cli.JOIN_TYPE_END_DEVICE)
+
+verify(leader.get_state() == 'leader')
+verify(br1.get_state() == 'router')
+verify(br2.get_state() == 'router')
+verify(c1.get_state() == 'child')
+verify(c2.get_state() == 'child')
+verify(c3.get_state() == 'child')
+verify(c4.get_state() == 'child')
+
+nodes_non_br = [leader, c1, c2, c3, c4]
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test implementation
+
+# Start the first BR
+
+br1.srp_server_set_addr_mode('unicast')
+br1.srp_server_auto_enable()
+
+br1.br_init(IF_INDEX, 1)
+br1.br_enable()
+
+time.sleep(1)
+verify(br1.br_get_state() == 'running')
+
+br1_local_omr = br1.br_get_local_omrprefix()
+br1_favored_omr = br1.br_get_favored_omrprefix().split()[0]
+verify(br1_local_omr == br1_favored_omr)
+
+br1_local_onlink = br1.br_get_local_onlinkprefix()
+br1_favored_onlink = br1.br_get_favored_onlinkprefix().split()[0]
+verify(br1_local_onlink == br1_favored_onlink)
+
+# Start the second BR
+
+br2.br_init(IF_INDEX, 1)
+br2.br_enable()
+
+time.sleep(1)
+verify(br2.br_get_state() == 'running')
+
+br2_local_omr = br2.br_get_local_omrprefix()
+br2_favored_omr = br2.br_get_favored_omrprefix().split()[0]
+verify(br2_favored_omr == br1_favored_omr)
+
+br2_favored_onlink = br2.br_get_favored_onlinkprefix().split()[0]
+verify(br2_favored_onlink == br1_favored_onlink)
+
+verify(br1.srp_server_get_state() == 'running')
+verify(br2.srp_server_get_state() == 'disabled')
+
+# Register SRP services on all nodes
+
+for node in nodes_non_br:
+ verify(node.srp_client_get_auto_start_mode() == 'Enabled')
+ node.srp_client_set_host_name('host' + str(node.index))
+ node.srp_client_enable_auto_host_address()
+ node.srp_client_add_service('srv' + str(node.index), '_test._udp', 777, 0, 0)
+
+time.sleep(1)
+
+hosts = br1.srp_server_get_hosts()
+verify(len(hosts) == len(nodes_non_br))
+
+services = br1.srp_server_get_services()
+verify(len(services) == len(nodes_non_br))
+
+# Ensure that all registered addresses are derived from BR1 OMR.
+
+for host in hosts:
+ verify(host['addresses'][0].startswith(br1_local_omr[:-4]))
+
+# Start SRP server on BR2
+
+br2.srp_server_set_addr_mode('unicast')
+br2.srp_server_auto_enable()
+
+time.sleep(1)
+
+verify(br2.srp_server_get_state() == 'running')
+
+# De-activate BR1
+
+br1.br_disable()
+br1.thread_stop()
+br1.interface_down()
+del br1
+
+c1.allowlist_node(br2)
+br2.allowlist_node(c1)
+
+# Wait long enough for BR2 to take over
+
+time.sleep(5)
+
+# Validate that everything is registered with BR2
+
+hosts = br2.srp_server_get_hosts()
+verify(len(hosts) == len(nodes_non_br))
+
+services = br2.srp_server_get_services()
+verify(len(services) == len(nodes_non_br))
+
+# Ensure that all registered addresses are now derived from BR2
+# OMR prefix.
+
+for host in hosts:
+ verify(host['addresses'][0].startswith(br2_local_omr[:-4]))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test finished
+
+cli.Node.finalize_all_nodes()
+
+print('\'{}\' passed.'.format(test_name))
diff --git a/tests/toranj/cli/test-502-multi-br-leader-failure-recovery.py b/tests/toranj/cli/test-502-multi-br-leader-failure-recovery.py
new file mode 100755
index 0000000..71d2ebc
--- /dev/null
+++ b/tests/toranj/cli/test-502-multi-br-leader-failure-recovery.py
@@ -0,0 +1,210 @@
+#!/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:
+#
+# Network with two BRs, BR1 acting as leader. Removing BR1 ensuring BR2 taking over.
+#
+# ________________
+# / \
+# br1 --- router --- br2
+# / / \ \
+# c1 c2 c3 c4
+#
+
+test_name = __file__[:-3] if __file__.endswith('.py') else __file__
+print('-' * 120)
+print('Starting \'{}\''.format(test_name))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Creating `cli.Nodes` instances
+
+speedup = 60
+cli.Node.set_time_speedup_factor(speedup)
+
+br1 = cli.Node()
+br2 = cli.Node()
+router = cli.Node()
+c1 = cli.Node()
+c2 = cli.Node()
+c3 = cli.Node()
+c4 = cli.Node()
+
+IF_INDEX = 1
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Form topology
+
+br1.allowlist_node(router)
+br1.allowlist_node(br2)
+br1.allowlist_node(c1)
+
+br2.allowlist_node(router)
+br2.allowlist_node(br1)
+br2.allowlist_node(c4)
+
+router.allowlist_node(br1)
+router.allowlist_node(br2)
+router.allowlist_node(c2)
+router.allowlist_node(c3)
+
+c1.allowlist_node(br1)
+
+c2.allowlist_node(router)
+c3.allowlist_node(router)
+
+c4.allowlist_node(br2)
+
+br1.form("multi-br")
+br2.join(br1)
+router.join(br1)
+c1.join(br1, cli.JOIN_TYPE_END_DEVICE)
+c2.join(br1, cli.JOIN_TYPE_END_DEVICE)
+c3.join(br1, cli.JOIN_TYPE_END_DEVICE)
+c4.join(br1, cli.JOIN_TYPE_END_DEVICE)
+
+verify(br1.get_state() == 'leader')
+verify(br2.get_state() == 'router')
+verify(router.get_state() == 'router')
+verify(c1.get_state() == 'child')
+verify(c2.get_state() == 'child')
+verify(c3.get_state() == 'child')
+verify(c4.get_state() == 'child')
+
+nodes_non_br = [router, c1, c2, c3, c4]
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test implementation
+
+# Start the first BR
+
+br1.srp_server_set_addr_mode('unicast')
+br1.srp_server_auto_enable()
+
+br1.br_init(IF_INDEX, 1)
+br1.br_enable()
+
+time.sleep(1)
+verify(br1.br_get_state() == 'running')
+
+br1_local_omr = br1.br_get_local_omrprefix()
+br1_favored_omr = br1.br_get_favored_omrprefix().split()[0]
+verify(br1_local_omr == br1_favored_omr)
+
+br1_local_onlink = br1.br_get_local_onlinkprefix()
+br1_favored_onlink = br1.br_get_favored_onlinkprefix().split()[0]
+verify(br1_local_onlink == br1_favored_onlink)
+
+# Start the second BR
+
+br2.br_init(IF_INDEX, 1)
+br2.br_enable()
+
+time.sleep(1)
+verify(br2.br_get_state() == 'running')
+
+br2_local_omr = br2.br_get_local_omrprefix()
+br2_favored_omr = br2.br_get_favored_omrprefix().split()[0]
+verify(br2_favored_omr == br1_favored_omr)
+
+br2_favored_onlink = br2.br_get_favored_onlinkprefix().split()[0]
+verify(br2_favored_onlink == br1_favored_onlink)
+
+verify(br1.srp_server_get_state() == 'running')
+verify(br2.srp_server_get_state() == 'disabled')
+
+# Register SRP services on all nodes
+
+for node in nodes_non_br:
+ verify(node.srp_client_get_auto_start_mode() == 'Enabled')
+ node.srp_client_set_host_name('host' + str(node.index))
+ node.srp_client_enable_auto_host_address()
+ node.srp_client_add_service('srv' + str(node.index), '_test._udp', 777, 0, 0)
+
+time.sleep(1)
+
+hosts = br1.srp_server_get_hosts()
+verify(len(hosts) == len(nodes_non_br))
+
+services = br1.srp_server_get_services()
+verify(len(services) == len(nodes_non_br))
+
+# Ensure that all registered addresses are derived from BR1 OMR.
+
+for host in hosts:
+ verify(host['addresses'][0].startswith(br1_local_omr[:-4]))
+
+# Start SRP server on BR2
+
+br2.srp_server_set_addr_mode('unicast')
+br2.srp_server_auto_enable()
+
+time.sleep(1)
+
+verify(br2.srp_server_get_state() == 'running')
+
+# De-activate BR1
+
+br1.br_disable()
+br1.thread_stop()
+br1.interface_down()
+del br1
+
+c1.allowlist_node(br2)
+br2.allowlist_node(c1)
+
+# Wait long enough for BR2 to take over
+
+time.sleep(5)
+
+# Validate that everything is registered with BR2
+
+hosts = br2.srp_server_get_hosts()
+verify(len(hosts) == len(nodes_non_br))
+
+services = br2.srp_server_get_services()
+verify(len(services) == len(nodes_non_br))
+
+# Ensure that all registered addresses are now derived from BR2
+# OMR prefix.
+
+for host in hosts:
+ verify(host['addresses'][0].startswith(br2_local_omr[:-4]))
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Test finished
+
+cli.Node.finalize_all_nodes()
+
+print('\'{}\' passed.'.format(test_name))
diff --git a/tests/toranj/cli/test-602-channel-manager-channel-select.py b/tests/toranj/cli/test-602-channel-manager-channel-select.py
index 0ddf358..596cfdc 100755
--- a/tests/toranj/cli/test-602-channel-manager-channel-select.py
+++ b/tests/toranj/cli/test-602-channel-manager-channel-select.py
@@ -66,6 +66,10 @@
verify(int(node.get_channel()) == channel)
+delay = int(node.cli('channel manager delay')[0])
+# add kRequestStartJitterInterval=10000ms to expected channel manager delay
+delay += 10 / speedup
+
check_channel()
all_channels_mask = int('0x7fff800', 0)
@@ -99,7 +103,7 @@
result = cli.Node.parse_list(node.cli('channel manager'))
verify(result['channel'] == '11')
channel = 11
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
# Set channels 12-15 as favorable and request a channel select, verify
# that channel is switched to 12.
@@ -112,13 +116,13 @@
channel = 25
node.cli('channel manager change', channel)
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
node.cli('channel manager select 1')
result = cli.Node.parse_list(node.cli('channel manager'))
verify(result['channel'] == '12')
channel = 12
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
# Set channels 15-17 as favorables and request a channel select,
# verify that channel is switched to 11.
@@ -129,7 +133,7 @@
channel = 25
node.cli('channel manager change', channel)
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
node.cli('channel manager favored', chan_15_to_17_mask)
@@ -137,7 +141,7 @@
result = cli.Node.parse_list(node.cli('channel manager'))
verify(result['channel'] == '11')
channel = 11
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
# Set channels 12-15 as favorable and request a channel select, verify
# that channel is not switched.
@@ -145,10 +149,11 @@
node.cli('channel manager favored', chan_12_to_15_mask)
node.cli('channel manager select 1')
+
result = cli.Node.parse_list(node.cli('channel manager'))
verify(result['channel'] == '11')
channel = 11
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
# Starting from channel 12 and issuing a channel select (which would
# pick 11 as best channel). However, since quality difference between
@@ -157,14 +162,60 @@
channel = 12
node.cli('channel manager change', channel)
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
node.cli('channel manager favored', all_channels_mask)
node.cli('channel manager select 1')
result = cli.Node.parse_list(node.cli('channel manager'))
verify(result['channel'] == '12')
-verify_within(check_channel, 2)
+verify_within(check_channel, delay)
+
+# -----------------------------------------------------------------------------------------------------------------------
+# Auto Select
+
+# Set channel manager cca failure rate threshold to 0
+# as we cannot control cca success in simulation
+node.cli('channel manager threshold 0')
+
+# Set short channel selection interval to speedup
+interval = 30
+node.cli(f'channel manager interval {interval}')
+
+# Set channels 15-17 as favorable and request a channel select, verify
+# that channel is switched to 11.
+
+channel = 25
+node.cli('channel manager change', channel)
+verify_within(check_channel, delay)
+node.cli('channel manager favored', chan_15_to_17_mask)
+
+# Active auto channel selection
+node.cli('channel manager auto 1')
+
+channel = 11
+result = cli.Node.parse_list(node.cli('channel manager'))
+verify(result['auto'] == '1')
+verify(result['channel'] == str(channel))
+
+verify_within(check_channel, delay)
+
+# while channel selection timer is running change to channel 25,
+# set channels 12-15 as favorable, wait for auto channel selection
+# and verify that channel is switched to 12.
+
+node.cli('channel manager favored', chan_12_to_15_mask)
+channel = 25
+node.cli('channel manager change', channel)
+
+# wait for timeout of auto selection timer
+time.sleep(2 * interval / speedup)
+
+channel = 12
+result = cli.Node.parse_list(node.cli('channel manager'))
+verify(result['channel'] == str(channel))
+
+verify_within(check_channel, delay)
# -----------------------------------------------------------------------------------------------------------------------
# Test finished
diff --git a/tests/toranj/openthread-core-toranj-config-posix.h b/tests/toranj/openthread-core-toranj-config-posix.h
index 60e8677..2fced00 100644
--- a/tests/toranj/openthread-core-toranj-config-posix.h
+++ b/tests/toranj/openthread-core-toranj-config-posix.h
@@ -39,6 +39,16 @@
#define OPENTHREAD_CONFIG_PLATFORM_INFO "POSIX-toranj"
+#ifdef __linux__
+#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 1
+#endif
+
+#ifndef OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE
+#define OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE 1
+#endif
+
+#define OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE 1
+
#define OPENTHREAD_CONFIG_LOG_OUTPUT OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED
#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1
diff --git a/tests/toranj/openthread-core-toranj-config-simulation.h b/tests/toranj/openthread-core-toranj-config-simulation.h
index bc933d1..f564e0b 100644
--- a/tests/toranj/openthread-core-toranj-config-simulation.h
+++ b/tests/toranj/openthread-core-toranj-config-simulation.h
@@ -61,7 +61,7 @@
#define OPENTHREAD_CONFIG_DNS_DSO_ENABLE 1
-#define OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE 1
+#define OPENTHREAD_CONFIG_SRP_SERVER_ADVERTISING_PROXY_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#define OPENTHREAD_CONFIG_PLATFORM_DNSSD_ENABLE 1
@@ -69,6 +69,8 @@
#define OPENTHREAD_CONFIG_BORDER_ROUTING_USE_HEAP_ENABLE 1
+#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 1
+
#define OPENTHREAD_CONFIG_RADIO_STATS_ENABLE 0
#endif /* OPENTHREAD_CORE_TORANJ_CONFIG_SIMULATION_H_ */
diff --git a/tests/toranj/openthread-core-toranj-config.h b/tests/toranj/openthread-core-toranj-config.h
index f2bcbb8..c1a9689 100644
--- a/tests/toranj/openthread-core-toranj-config.h
+++ b/tests/toranj/openthread-core-toranj-config.h
@@ -49,22 +49,32 @@
#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1
+#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#define OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE 1
+#endif
-#define OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE 1
+#define OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#define OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE 1
#define OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE 1
+#define OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE 1
+
#define OPENTHREAD_CONFIG_MESH_DIAG_ENABLE 1
+#define OPENTHREAD_CONFIG_BLE_TCAT_ENABLE 1
+
#define OPENTHREAD_CONFIG_COMMISSIONER_ENABLE 1
#define OPENTHREAD_CONFIG_COMMISSIONER_MAX_JOINER_ENTRIES 4
#define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 1
+#define OPENTHREAD_CONFIG_BORDER_AGENT_EPHEMERAL_KEY_ENABLE 1
+
+#define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 1
+
#define OPENTHREAD_CONFIG_DIAG_ENABLE 1
#define OPENTHREAD_CONFIG_JOINER_ENABLE 1
@@ -170,8 +180,6 @@
#define OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE 1
-#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 1
-
#define OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK 1
#define OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE 1
diff --git a/tests/toranj/start.sh b/tests/toranj/start.sh
index 5915459..04cef96 100755
--- a/tests/toranj/start.sh
+++ b/tests/toranj/start.sh
@@ -192,7 +192,12 @@
run cli/test-025-mesh-local-prefix-change.py
run cli/test-026-coaps-conn-limit.py
run cli/test-027-slaac-address.py
+ run cli/test-028-border-agent-ephemeral-key.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
+ run cli/test-501-multi-br-failure-recovery.py
+ run cli/test-502-multi-br-leader-failure-recovery.py
run cli/test-601-channel-manager-channel-change.py
# Skip the "channel-select" test on a TREL only radio link, since it
# requires energy scan which is not supported in this case.
diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt
index 1a9b182..92337b3 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -1180,6 +1180,27 @@
add_test(NAME ot-test-string COMMAND ot-test-string)
+add_executable(ot-test-tcat
+ test_tcat.cpp
+)
+
+target_include_directories(ot-test-tcat
+ PRIVATE
+ ${COMMON_INCLUDES}
+)
+
+target_compile_options(ot-test-tcat
+ PRIVATE
+ ${COMMON_COMPILE_OPTIONS}
+)
+
+target_link_libraries(ot-test-tcat
+ PRIVATE
+ ${COMMON_LIBS}
+)
+
+add_test(NAME ot-test-tcat COMMAND ot-test-tcat)
+
add_executable(ot-test-timer
test_timer.cpp
)
diff --git a/tests/unit/test_network_data.cpp b/tests/unit/test_network_data.cpp
index 2868709..4740a2f 100644
--- a/tests/unit/test_network_data.cpp
+++ b/tests/unit/test_network_data.cpp
@@ -86,32 +86,32 @@
(aConfig1.mDefaultRoute == aConfig2.mDefaultRoute) && (aConfig1.mOnMesh == aConfig2.mOnMesh);
}
-template <uint8_t kLength>
-void VerifyRlocsArray(const uint16_t *aRlocs, uint16_t aRlocsLength, const uint16_t (&aExpectedRlocs)[kLength])
+template <uint8_t kLength> void VerifyRlocsArray(const Rlocs &aRlocs, const uint16_t (&aExpectedRlocs)[kLength])
{
- VerifyOrQuit(aRlocsLength == kLength);
+ VerifyOrQuit(aRlocs.GetLength() == kLength);
printf("\nRLOCs: { ");
- for (uint16_t index = 0; index < aRlocsLength; index++)
+ for (uint16_t rloc : aRlocs)
{
- VerifyOrQuit(aRlocs[index] == aExpectedRlocs[index]);
- printf("0x%04x ", aRlocs[index]);
+ printf("0x%04x ", rloc);
}
printf("}");
+
+ for (uint16_t index = 0; index < kLength; index++)
+ {
+ VerifyOrQuit(aRlocs.Contains(aExpectedRlocs[index]));
+ }
}
void TestNetworkDataIterator(void)
{
- static constexpr uint8_t kMaxRlocsArray = 10;
-
Instance *instance;
Iterator iter = kIteratorInit;
ExternalRouteConfig rconfig;
OnMeshPrefixConfig pconfig;
- uint16_t rlocs[kMaxRlocsArray];
- uint8_t rlocsLength;
+ Rlocs rlocs;
instance = testInitInstance();
VerifyOrQuit(instance != nullptr);
@@ -168,19 +168,25 @@
VerifyOrQuit(CompareExternalRouteConfig(rconfig, route));
}
- rlocsLength = GetArrayLength(rlocs);
- SuccessOrQuit(netData.FindBorderRouters(kAnyRole, rlocs, rlocsLength));
- VerifyRlocsArray(rlocs, rlocsLength, kRlocs);
+ netData.FindRlocs(kAnyBrOrServer, kAnyRole, rlocs);
+ VerifyRlocsArray(rlocs, kRlocs);
+
+ netData.FindRlocs(kAnyBrOrServer, kRouterRoleOnly, rlocs);
+ VerifyRlocsArray(rlocs, kRlocs);
+
+ netData.FindRlocs(kAnyBrOrServer, kChildRoleOnly, rlocs);
+ VerifyOrQuit(rlocs.GetLength() == 0);
+
+ netData.FindRlocs(kBrProvidingExternalIpConn, kAnyRole, rlocs);
+ VerifyRlocsArray(rlocs, kRlocs);
VerifyOrQuit(netData.CountBorderRouters(kAnyRole) == GetArrayLength(kRlocs));
- rlocsLength = GetArrayLength(rlocs);
- SuccessOrQuit(netData.FindBorderRouters(kRouterRoleOnly, rlocs, rlocsLength));
- VerifyRlocsArray(rlocs, rlocsLength, kRlocs);
+ netData.FindRlocs(kBrProvidingExternalIpConn, kRouterRoleOnly, rlocs);
+ VerifyRlocsArray(rlocs, kRlocs);
VerifyOrQuit(netData.CountBorderRouters(kRouterRoleOnly) == GetArrayLength(kRlocs));
- rlocsLength = GetArrayLength(rlocs);
- SuccessOrQuit(netData.FindBorderRouters(kChildRoleOnly, rlocs, rlocsLength));
- VerifyOrQuit(rlocsLength == 0);
+ netData.FindRlocs(kBrProvidingExternalIpConn, kChildRoleOnly, rlocs);
+ VerifyOrQuit(rlocs.GetLength() == 0);
VerifyOrQuit(netData.CountBorderRouters(kChildRoleOnly) == 0);
for (uint16_t rloc16 : kRlocs)
@@ -284,33 +290,29 @@
VerifyOrQuit(CompareExternalRouteConfig(rconfig, route));
}
- rlocsLength = GetArrayLength(rlocs);
- SuccessOrQuit(netData.FindBorderRouters(kAnyRole, rlocs, rlocsLength));
- VerifyRlocsArray(rlocs, rlocsLength, kRlocsAnyRole);
+ netData.FindRlocs(kAnyBrOrServer, kAnyRole, rlocs);
+ VerifyRlocsArray(rlocs, kRlocsAnyRole);
+
+ netData.FindRlocs(kAnyBrOrServer, kRouterRoleOnly, rlocs);
+ VerifyRlocsArray(rlocs, kRlocsRouterRole);
+
+ netData.FindRlocs(kAnyBrOrServer, kChildRoleOnly, rlocs);
+ VerifyRlocsArray(rlocs, kRlocsChildRole);
+
+ netData.FindRlocs(kBrProvidingExternalIpConn, kAnyRole, rlocs);
+ VerifyRlocsArray(rlocs, kRlocsAnyRole);
VerifyOrQuit(netData.CountBorderRouters(kAnyRole) == GetArrayLength(kRlocsAnyRole));
- rlocsLength = GetArrayLength(rlocs);
- SuccessOrQuit(netData.FindBorderRouters(kRouterRoleOnly, rlocs, rlocsLength));
- VerifyRlocsArray(rlocs, rlocsLength, kRlocsRouterRole);
+ netData.FindRlocs(kBrProvidingExternalIpConn, kRouterRoleOnly, rlocs);
+ VerifyRlocsArray(rlocs, kRlocsRouterRole);
VerifyOrQuit(netData.CountBorderRouters(kRouterRoleOnly) == GetArrayLength(kRlocsRouterRole));
- rlocsLength = GetArrayLength(rlocs);
- SuccessOrQuit(netData.FindBorderRouters(kChildRoleOnly, rlocs, rlocsLength));
- VerifyRlocsArray(rlocs, rlocsLength, kRlocsChildRole);
+ netData.FindRlocs(kBrProvidingExternalIpConn, kChildRoleOnly, rlocs);
+ VerifyRlocsArray(rlocs, kRlocsChildRole);
VerifyOrQuit(netData.CountBorderRouters(kChildRoleOnly) == GetArrayLength(kRlocsChildRole));
- // Test failure case when given array is smaller than number of RLOCs.
- rlocsLength = GetArrayLength(kRlocsAnyRole) - 1;
- VerifyOrQuit(netData.FindBorderRouters(kAnyRole, rlocs, rlocsLength) == kErrorNoBufs);
- VerifyOrQuit(rlocsLength == GetArrayLength(kRlocsAnyRole) - 1);
- for (uint8_t index = 0; index < rlocsLength; index++)
- {
- VerifyOrQuit(rlocs[index] == kRlocsAnyRole[index]);
- }
-
- rlocsLength = GetArrayLength(kRlocsAnyRole);
- SuccessOrQuit(netData.FindBorderRouters(kAnyRole, rlocs, rlocsLength));
- VerifyRlocsArray(rlocs, rlocsLength, kRlocsAnyRole);
+ netData.FindRlocs(kBrProvidingExternalIpConn, kAnyRole, rlocs);
+ VerifyRlocsArray(rlocs, kRlocsAnyRole);
for (uint16_t rloc16 : kRlocsAnyRole)
{
@@ -434,10 +436,13 @@
},
};
- const uint16_t kRlocsAnyRole[] = {0xec00, 0x2801, 0x2800};
- const uint16_t kRlocsRouterRole[] = {0xec00, 0x2800};
- const uint16_t kRlocsChildRole[] = {0x2801};
- const uint16_t kNonExistingRlocs[] = {0x6000, 0x0000, 0x2806, 0x4c00};
+ const uint16_t kRlocsAnyRole[] = {0xec00, 0x2801, 0x2800, 0x4c00};
+ const uint16_t kRlocsRouterRole[] = {0xec00, 0x2800, 0x4c00};
+ const uint16_t kRlocsChildRole[] = {0x2801};
+ const uint16_t kBrRlocsAnyRole[] = {0xec00, 0x2801, 0x2800};
+ const uint16_t kBrRlocsRouterRole[] = {0xec00, 0x2800};
+ const uint16_t kBrRlocsChildRole[] = {0x2801};
+ const uint16_t kNonExistingRlocs[] = {0x6000, 0x0000, 0x2806, 0x4c00};
NetworkData netData(*instance, kNetworkData, sizeof(kNetworkData));
@@ -462,22 +467,28 @@
VerifyOrQuit(CompareOnMeshPrefixConfig(pconfig, prefix));
}
- rlocsLength = GetArrayLength(rlocs);
- SuccessOrQuit(netData.FindBorderRouters(kAnyRole, rlocs, rlocsLength));
- VerifyRlocsArray(rlocs, rlocsLength, kRlocsAnyRole);
- VerifyOrQuit(netData.CountBorderRouters(kAnyRole) == GetArrayLength(kRlocsAnyRole));
+ netData.FindRlocs(kAnyBrOrServer, kAnyRole, rlocs);
+ VerifyRlocsArray(rlocs, kRlocsAnyRole);
- rlocsLength = GetArrayLength(rlocs);
- SuccessOrQuit(netData.FindBorderRouters(kRouterRoleOnly, rlocs, rlocsLength));
- VerifyRlocsArray(rlocs, rlocsLength, kRlocsRouterRole);
- VerifyOrQuit(netData.CountBorderRouters(kRouterRoleOnly) == GetArrayLength(kRlocsRouterRole));
+ netData.FindRlocs(kAnyBrOrServer, kRouterRoleOnly, rlocs);
+ VerifyRlocsArray(rlocs, kRlocsRouterRole);
- rlocsLength = GetArrayLength(rlocs);
- SuccessOrQuit(netData.FindBorderRouters(kChildRoleOnly, rlocs, rlocsLength));
- VerifyRlocsArray(rlocs, rlocsLength, kRlocsChildRole);
- VerifyOrQuit(netData.CountBorderRouters(kChildRoleOnly) == GetArrayLength(kRlocsChildRole));
+ netData.FindRlocs(kAnyBrOrServer, kChildRoleOnly, rlocs);
+ VerifyRlocsArray(rlocs, kRlocsChildRole);
- for (uint16_t rloc16 : kRlocsAnyRole)
+ netData.FindRlocs(kBrProvidingExternalIpConn, kAnyRole, rlocs);
+ VerifyRlocsArray(rlocs, kBrRlocsAnyRole);
+ VerifyOrQuit(netData.CountBorderRouters(kAnyRole) == GetArrayLength(kBrRlocsAnyRole));
+
+ netData.FindRlocs(kBrProvidingExternalIpConn, kRouterRoleOnly, rlocs);
+ VerifyRlocsArray(rlocs, kBrRlocsRouterRole);
+ VerifyOrQuit(netData.CountBorderRouters(kRouterRoleOnly) == GetArrayLength(kBrRlocsRouterRole));
+
+ netData.FindRlocs(kBrProvidingExternalIpConn, kChildRoleOnly, rlocs);
+ VerifyRlocsArray(rlocs, kBrRlocsChildRole);
+ VerifyOrQuit(netData.CountBorderRouters(kChildRoleOnly) == GetArrayLength(kBrRlocsChildRole));
+
+ for (uint16_t rloc16 : kBrRlocsAnyRole)
{
VerifyOrQuit(netData.ContainsBorderRouterWithRloc(rloc16));
}
@@ -681,17 +692,34 @@
{"fdde:ad00:beef:0:0:ff:fe00:6c00", 0xcd12, Service::DnsSrpUnicast::kFromServerData, 0x6c00},
};
+ const uint16_t kExpectedRlocs[] = {0x6c00, 0x2800, 0x4c00, 0x0000};
+
const uint8_t kPreferredAnycastEntryIndex = 2;
Service::Manager &manager = instance->Get<Service::Manager>();
Service::Manager::Iterator iterator;
Service::DnsSrpAnycast::Info anycastInfo;
Service::DnsSrpUnicast::Info unicastInfo;
+ Rlocs rlocs;
reinterpret_cast<TestLeader &>(instance->Get<Leader>()).Populate(kNetworkData, sizeof(kNetworkData));
DumpBuffer("netdata", kNetworkData, sizeof(kNetworkData));
+ // Verify `FindRlocs()`
+
+ instance->Get<Leader>().FindRlocs(kAnyBrOrServer, kAnyRole, rlocs);
+ VerifyRlocsArray(rlocs, kExpectedRlocs);
+
+ instance->Get<Leader>().FindRlocs(kAnyBrOrServer, kRouterRoleOnly, rlocs);
+ VerifyRlocsArray(rlocs, kExpectedRlocs);
+
+ instance->Get<Leader>().FindRlocs(kAnyBrOrServer, kChildRoleOnly, rlocs);
+ VerifyOrQuit(rlocs.GetLength() == 0);
+
+ instance->Get<Leader>().FindRlocs(kBrProvidingExternalIpConn, kAnyRole, rlocs);
+ VerifyOrQuit(rlocs.GetLength() == 0);
+
// Verify all the "DNS/SRP Anycast Service" entries in Network Data
printf("\n- - - - - - - - - - - - - - - - - - - -");
diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp
index 40e23ce..2904f60 100644
--- a/tests/unit/test_platform.cpp
+++ b/tests/unit/test_platform.cpp
@@ -694,39 +694,39 @@
otError otPlatBleEnable(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
- return OT_ERROR_NOT_IMPLEMENTED;
+ return OT_ERROR_NONE;
}
otError otPlatBleDisable(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
- return OT_ERROR_NOT_IMPLEMENTED;
+ return OT_ERROR_NONE;
}
otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aInterval);
- return OT_ERROR_NOT_IMPLEMENTED;
+ return OT_ERROR_NONE;
}
otError otPlatBleGapAdvStop(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
- return OT_ERROR_NOT_IMPLEMENTED;
+ return OT_ERROR_NONE;
}
otError otPlatBleGapDisconnect(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
- return OT_ERROR_NOT_IMPLEMENTED;
+ return OT_ERROR_NONE;
}
otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aMtu);
- return OT_ERROR_NOT_IMPLEMENTED;
+ return OT_ERROR_NONE;
}
otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket)
@@ -734,7 +734,7 @@
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aHandle);
OT_UNUSED_VARIABLE(aPacket);
- return OT_ERROR_NOT_IMPLEMENTED;
+ return OT_ERROR_NONE;
}
#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
diff --git a/tests/unit/test_pskc.cpp b/tests/unit/test_pskc.cpp
index 2515637..b1a3877 100644
--- a/tests/unit/test_pskc.cpp
+++ b/tests/unit/test_pskc.cpp
@@ -34,71 +34,81 @@
#include "test_util.h"
namespace ot {
+namespace MeshCoP {
#if OPENTHREAD_FTD
void TestMinimumPassphrase(void)
{
- ot::Pskc pskc;
- const uint8_t expectedPskc[] = {0x44, 0x98, 0x8e, 0x22, 0xcf, 0x65, 0x2e, 0xee,
- 0xcc, 0xd1, 0xe4, 0xc0, 0x1d, 0x01, 0x54, 0xf8};
- const otExtendedPanId xpanid = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}};
- const char passphrase[] = "123456";
- otInstance *instance = testInitInstance();
- SuccessOrQuit(ot::MeshCoP::GeneratePskc(passphrase,
- *reinterpret_cast<const ot::MeshCoP::NetworkName *>("OpenThread"),
- static_cast<const ot::MeshCoP::ExtendedPanId &>(xpanid), pskc));
- VerifyOrQuit(memcmp(pskc.m8, expectedPskc, OT_PSKC_MAX_SIZE) == 0);
+ static const otExtendedPanId kExtPanId = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}};
+ static const otNetworkName kNetworkName = {{'O', 'p', 'e', 'n', 'T', 'h', 'r', 'e', 'a', 'd', '\0'}};
+ static const char kPassphrase[] = "123456";
+
+ static const otPskc kExpectedPskc = {
+ {0x44, 0x98, 0x8e, 0x22, 0xcf, 0x65, 0x2e, 0xee, 0xcc, 0xd1, 0xe4, 0xc0, 0x1d, 0x01, 0x54, 0xf8}};
+
+ Instance *instance = testInitInstance();
+ Pskc pskc;
+
+ SuccessOrQuit(GeneratePskc(kPassphrase, AsCoreType(&kNetworkName), AsCoreType(&kExtPanId), pskc));
+ VerifyOrQuit(pskc == AsCoreType(&kExpectedPskc));
+
testFreeInstance(instance);
}
void TestMaximumPassphrase(void)
{
- ot::Pskc pskc;
- const uint8_t expectedPskc[] = {0x9e, 0x81, 0xbd, 0x35, 0xa2, 0x53, 0x76, 0x2f,
- 0x80, 0xee, 0x04, 0xff, 0x2f, 0xa2, 0x85, 0xe9};
- const otExtendedPanId xpanid = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}};
- const char passphrase[] = "1234567812345678"
- "1234567812345678"
- "1234567812345678"
- "1234567812345678"
- "1234567812345678"
- "1234567812345678"
- "1234567812345678"
- "1234567812345678"
- "1234567812345678"
- "1234567812345678"
- "1234567812345678"
- "1234567812345678"
- "1234567812345678"
- "1234567812345678"
- "1234567812345678"
- "123456781234567";
+ static const otExtendedPanId kExtPanId = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}};
+ static const otNetworkName kNetworkName = {{'O', 'p', 'e', 'n', 'T', 'h', 'r', 'e', 'a', 'd', '\0'}};
- otInstance *instance = testInitInstance();
- SuccessOrQuit(ot::MeshCoP::GeneratePskc(passphrase,
- *reinterpret_cast<const ot::MeshCoP::NetworkName *>("OpenThread"),
- static_cast<const ot::MeshCoP::ExtendedPanId &>(xpanid), pskc));
- VerifyOrQuit(memcmp(pskc.m8, expectedPskc, sizeof(pskc.m8)) == 0);
+ static const char kPassphrase[] = "1234567812345678"
+ "1234567812345678"
+ "1234567812345678"
+ "1234567812345678"
+ "1234567812345678"
+ "1234567812345678"
+ "1234567812345678"
+ "1234567812345678"
+ "1234567812345678"
+ "1234567812345678"
+ "1234567812345678"
+ "1234567812345678"
+ "1234567812345678"
+ "1234567812345678"
+ "1234567812345678"
+ "123456781234567";
+
+ static const otPskc kExpectedPskc = {
+ {0x9e, 0x81, 0xbd, 0x35, 0xa2, 0x53, 0x76, 0x2f, 0x80, 0xee, 0x04, 0xff, 0x2f, 0xa2, 0x85, 0xe9}};
+
+ Instance *instance = testInitInstance();
+ Pskc pskc;
+
+ SuccessOrQuit(GeneratePskc(kPassphrase, AsCoreType(&kNetworkName), AsCoreType(&kExtPanId), pskc));
+ VerifyOrQuit(pskc == AsCoreType(&kExpectedPskc));
+
testFreeInstance(instance);
}
void TestExampleInSpec(void)
{
- ot::Pskc pskc;
- const uint8_t expectedPskc[] = {0xc3, 0xf5, 0x93, 0x68, 0x44, 0x5a, 0x1b, 0x61,
- 0x06, 0xbe, 0x42, 0x0a, 0x70, 0x6d, 0x4c, 0xc9};
- const otExtendedPanId xpanid = {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}};
- const char passphrase[] = "12SECRETPASSWORD34";
+ static const otExtendedPanId kExtPanId = {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}};
+ static const otNetworkName kNetworkName = {{'T', 'e', 's', 't', ' ', 'N', 'e', 't', 'w', 'o', 'r', 'k', '\0'}};
+ static const char kPassphrase[] = "12SECRETPASSWORD34";
- otInstance *instance = testInitInstance();
- SuccessOrQuit(ot::MeshCoP::GeneratePskc(passphrase,
- *reinterpret_cast<const ot::MeshCoP::NetworkName *>("Test Network"),
- static_cast<const ot::MeshCoP::ExtendedPanId &>(xpanid), pskc));
- VerifyOrQuit(memcmp(pskc.m8, expectedPskc, sizeof(pskc.m8)) == 0);
+ static const otPskc kExpectedPskc = {
+ {0xc3, 0xf5, 0x93, 0x68, 0x44, 0x5a, 0x1b, 0x61, 0x06, 0xbe, 0x42, 0x0a, 0x70, 0x6d, 0x4c, 0xc9}};
+
+ Instance *instance = testInitInstance();
+ Pskc pskc;
+
+ SuccessOrQuit(GeneratePskc(kPassphrase, AsCoreType(&kNetworkName), AsCoreType(&kExtPanId), pskc));
+ VerifyOrQuit(pskc == AsCoreType(&kExpectedPskc));
+
testFreeInstance(instance);
}
+} // namespace MeshCoP
} // namespace ot
#endif // OPENTHREAD_FTD
@@ -106,9 +116,9 @@
int main(void)
{
#if OPENTHREAD_FTD
- ot::TestMinimumPassphrase();
- ot::TestMaximumPassphrase();
- ot::TestExampleInSpec();
+ ot::MeshCoP::TestMinimumPassphrase();
+ ot::MeshCoP::TestMaximumPassphrase();
+ ot::MeshCoP::TestExampleInSpec();
printf("All tests passed\n");
#else
printf("PSKc generation is not supported on non-ftd build\n");
diff --git a/tests/unit/test_routing_manager.cpp b/tests/unit/test_routing_manager.cpp
index df7c2b2..a474de4 100644
--- a/tests/unit/test_routing_manager.cpp
+++ b/tests/unit/test_routing_manager.cpp
@@ -126,7 +126,7 @@
static uint8_t sRadioTxFramePsdu[OT_RADIO_FRAME_MAX_SIZE];
static bool sRadioTxOngoing = false;
-using Icmp6Packet = Ip6::Nd::RouterAdvertMessage::Icmp6Packet;
+using Icmp6Packet = Ip6::Nd::RouterAdvert::Icmp6Packet;
enum ExpectedPio
{
@@ -160,6 +160,12 @@
ExpectedPio sExpectedPio; // Expected PIO in the emitted RA by BR (MUST be seen in RA to set `sRaValidated`).
uint32_t sOnLinkLifetime; // Valid lifetime for local on-link prefix from the last processed RA.
+// Indicate whether or not to check the emitted RA header (default route) lifetime
+bool sCheckRaHeaderLifetime;
+
+// Expected default route lifetime in emitted RA header by BR.
+uint32_t sExpectedRaHeaderLifetime;
+
enum ExpectedRaHeaderFlags
{
kRaHeaderFlagsSkipChecking, // Skip checking the RA header flags.
@@ -414,7 +420,7 @@
{
constexpr uint8_t kMaxPrefixes = 16;
- Ip6::Nd::RouterAdvertMessage raMsg(aPacket);
+ Ip6::Nd::RouterAdvert::RxMessage raMsg(aPacket);
bool sawExpectedPio = false;
Array<Ip6::Prefix, kMaxPrefixes> pioPrefixes;
Array<Ip6::Prefix, kMaxPrefixes> rioPrefixes;
@@ -424,7 +430,10 @@
VerifyOrQuit(raMsg.IsValid());
- VerifyOrQuit(raMsg.GetHeader().GetRouterLifetime() == 0);
+ if (sCheckRaHeaderLifetime)
+ {
+ VerifyOrQuit(raMsg.GetHeader().GetRouterLifetime() == sExpectedRaHeaderLifetime);
+ }
switch (sExpectedRaHeaderFlags)
{
@@ -570,7 +579,7 @@
void LogRouterAdvert(const Icmp6Packet &aPacket)
{
- Ip6::Nd::RouterAdvertMessage raMsg(aPacket);
+ Ip6::Nd::RouterAdvert::RxMessage raMsg(aPacket);
VerifyOrQuit(raMsg.IsValid());
@@ -854,17 +863,15 @@
bool mStubRouterFlag;
};
-template <size_t N>
-uint16_t BuildRouterAdvert(uint8_t (&aBuffer)[N],
- const Pio *aPios,
- uint16_t aNumPios,
- const Rio *aRios,
- uint16_t aNumRios,
- const DefaultRoute &aDefaultRoute,
- const RaFlags &aRaFlags)
+void BuildRouterAdvert(Ip6::Nd::RouterAdvert::TxMessage &aRaMsg,
+ const Pio *aPios,
+ uint16_t aNumPios,
+ const Rio *aRios,
+ uint16_t aNumRios,
+ const DefaultRoute &aDefaultRoute,
+ const RaFlags &aRaFlags)
{
- Ip6::Nd::RouterAdvertMessage::Header header;
- uint16_t length;
+ Ip6::Nd::RouterAdvert::Header header;
header.SetRouterLifetime(aDefaultRoute.mLifetime);
header.SetDefaultRouterPreference(aDefaultRoute.mPreference);
@@ -879,29 +886,22 @@
header.SetOtherConfigFlag();
}
+ SuccessOrQuit(aRaMsg.AppendHeader(header));
+
+ if (aRaFlags.mStubRouterFlag)
{
- Ip6::Nd::RouterAdvertMessage raMsg(header, aBuffer);
-
- if (aRaFlags.mStubRouterFlag)
- {
- SuccessOrQuit(raMsg.AppendFlagsExtensionOption(/* aStubRouterFlag */ true));
- }
-
- for (; aNumPios > 0; aPios++, aNumPios--)
- {
- SuccessOrQuit(
- raMsg.AppendPrefixInfoOption(aPios->mPrefix, aPios->mValidLifetime, aPios->mPreferredLifetime));
- }
-
- for (; aNumRios > 0; aRios++, aNumRios--)
- {
- SuccessOrQuit(raMsg.AppendRouteInfoOption(aRios->mPrefix, aRios->mValidLifetime, aRios->mPreference));
- }
-
- length = raMsg.GetAsPacket().GetLength();
+ SuccessOrQuit(aRaMsg.AppendFlagsExtensionOption(/* aStubRouterFlag */ true));
}
- return length;
+ for (; aNumPios > 0; aPios++, aNumPios--)
+ {
+ SuccessOrQuit(aRaMsg.AppendPrefixInfoOption(aPios->mPrefix, aPios->mValidLifetime, aPios->mPreferredLifetime));
+ }
+
+ for (; aNumRios > 0; aRios++, aNumRios--)
+ {
+ SuccessOrQuit(aRaMsg.AppendRouteInfoOption(aRios->mPrefix, aRios->mValidLifetime, aRios->mPreference));
+ }
}
void SendRouterAdvert(const Ip6::Address &aRouterAddress,
@@ -912,12 +912,15 @@
const DefaultRoute &aDefaultRoute,
const RaFlags &aRaFlags)
{
- uint8_t buffer[kMaxRaSize];
- uint16_t length = BuildRouterAdvert(buffer, aPios, aNumPios, aRios, aNumRios, aDefaultRoute, aRaFlags);
+ Ip6::Nd::RouterAdvert::TxMessage raMsg;
+ Icmp6Packet packet;
- SendRouterAdvert(aRouterAddress, buffer, length);
+ BuildRouterAdvert(raMsg, aPios, aNumPios, aRios, aNumRios, aDefaultRoute, aRaFlags);
+ raMsg.GetAsPacket(packet);
+
+ SendRouterAdvert(aRouterAddress, packet);
Log("Sending RA from router %s", aRouterAddress.ToString().AsCString());
- LogRouterAdvert(buffer, length);
+ LogRouterAdvert(packet);
}
template <uint16_t kNumPios, uint16_t kNumRios>
@@ -963,13 +966,16 @@
template <uint16_t kNumPios> void SendRouterAdvertToBorderRoutingProcessIcmp6Ra(const Pio (&aPios)[kNumPios])
{
- uint8_t buffer[kMaxRaSize];
- uint16_t length = BuildRouterAdvert(buffer, aPios, kNumPios, nullptr, 0,
- DefaultRoute(0, NetworkData::kRoutePreferenceMedium), RaFlags());
+ Ip6::Nd::RouterAdvert::TxMessage raMsg;
+ Icmp6Packet packet;
- otPlatBorderRoutingProcessIcmp6Ra(sInstance, buffer, length);
+ BuildRouterAdvert(raMsg, aPios, kNumPios, nullptr, 0, DefaultRoute(0, NetworkData::kRoutePreferenceMedium),
+ RaFlags());
+ raMsg.GetAsPacket(packet);
+
+ otPlatBorderRoutingProcessIcmp6Ra(sInstance, packet.GetBytes(), packet.GetLength());
Log("Passing RA to otPlatBorderRoutingProcessIcmp6Ra");
- LogRouterAdvert(buffer, length);
+ LogRouterAdvert(packet);
}
struct OnLinkPrefix : public Pio
@@ -1191,8 +1197,10 @@
sRaValidated = false;
sExpectedPio = kNoPio;
sExpectedRios.Clear();
- sRespondToNs = true;
- sExpectedRaHeaderFlags = kRaHeaderFlagsNone;
+ sRespondToNs = true;
+ sExpectedRaHeaderFlags = kRaHeaderFlagsNone;
+ sCheckRaHeaderLifetime = true;
+ sExpectedRaHeaderLifetime = 0;
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ensure device starts as leader.
@@ -2953,6 +2961,93 @@
FinalizeTest();
}
+void TestLearnRaHeader(void)
+{
+ Ip6::Prefix localOnLink;
+ Ip6::Prefix localOmr;
+ Ip6::Prefix onLinkPrefix = PrefixFromString("2000:abba:baba::", 64);
+ uint16_t heapAllocations;
+
+ Log("--------------------------------------------------------------------------------------------");
+ Log("TestLearnRaHeader");
+
+ InitTest();
+
+ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // Start Routing Manager. Check emitted RS and RA messages.
+
+ sRsEmitted = false;
+ sRaValidated = false;
+ sExpectedPio = kPioAdvertisingLocalOnLink;
+ sExpectedRios.Clear();
+
+ heapAllocations = sHeapAllocatedPtrs.GetLength();
+ SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().SetEnabled(true));
+
+ SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOnLinkPrefix(localOnLink));
+ SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().GetOmrPrefix(localOmr));
+
+ Log("Local on-link prefix is %s", localOnLink.ToString().AsCString());
+ Log("Local OMR prefix is %s", localOmr.ToString().AsCString());
+
+ sExpectedRios.Add(localOmr);
+
+ AdvanceTime(30000);
+
+ VerifyOrQuit(sRsEmitted);
+ VerifyOrQuit(sRaValidated);
+ VerifyOrQuit(sExpectedRios.SawAll());
+ Log("Received RA was validated");
+
+ VerifyDiscoveredRoutersIsEmpty();
+
+ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // Send an RA from the same address (another entity on the device)
+ // advertising a default route.
+
+ SendRouterAdvert(sInfraIfAddress, DefaultRoute(1000, NetworkData::kRoutePreferenceLow));
+
+ AdvanceTime(1);
+ VerifyDiscoveredRouters({InfraRouter(sInfraIfAddress, /* M */ false, /* O */ false, /* StubRouter */ false)});
+
+ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // RoutingManager should learn the header from the
+ // received RA (from same address) and start advertising
+ // the same default route lifetime in the emitted RAs.
+
+ sRaValidated = false;
+ sCheckRaHeaderLifetime = true;
+ sExpectedRaHeaderLifetime = 1000;
+
+ AdvanceTime(30 * 1000);
+ VerifyOrQuit(sRaValidated);
+
+ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // Wait for longer than entry lifetime (for it to expire) and
+ // make sure `RoutingManager` stops advertising default route.
+
+ sCheckRaHeaderLifetime = false;
+
+ AdvanceTime(1000 * 1000);
+
+ sRaValidated = false;
+ sCheckRaHeaderLifetime = true;
+ sExpectedRaHeaderLifetime = 0;
+
+ AdvanceTime(700 * 1000);
+ VerifyOrQuit(sRaValidated);
+
+ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+ SuccessOrQuit(sInstance->Get<BorderRouter::RoutingManager>().SetEnabled(false));
+ VerifyDiscoveredRoutersIsEmpty();
+
+ VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength());
+
+ Log("End of TestLearnRaHeader");
+ FinalizeTest();
+}
+
void TestConflictingPrefix(void)
{
static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x08}};
@@ -3924,6 +4019,7 @@
ot::TestConflictingPrefix();
ot::TestRouterNsProbe();
ot::TestLearningAndCopyingOfFlags();
+ ot::TestLearnRaHeader();
#if OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE
ot::TestSavedOnLinkPrefixes();
#endif
diff --git a/tests/unit/test_tcat.cpp b/tests/unit/test_tcat.cpp
new file mode 100644
index 0000000..6767a0f
--- /dev/null
+++ b/tests/unit/test_tcat.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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.
+ */
+
+#include "openthread-core-config.h"
+
+#include "test_platform.h"
+#include "test_util.h"
+
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+
+#include <openthread/ble_secure.h>
+
+#define OT_TCAT_X509_CERT \
+ "-----BEGIN CERTIFICATE-----\r\n" \
+ "MIIBmDCCAT+gAwIBAgIEAQIDBDAKBggqhkjOPQQDAjBvMQswCQYDVQQGEwJYWDEQ\r\n" \
+ "MA4GA1UECBMHTXlTdGF0ZTEPMA0GA1UEBxMGTXlDaXR5MQ8wDQYDVQQLEwZNeVVu\r\n" \
+ "aXQxETAPBgNVBAoTCE15VmVuZG9yMRkwFwYDVQQDExB3d3cubXl2ZW5kb3IuY29t\r\n" \
+ "MB4XDTIzMTAxNjEwMzk1NFoXDTI0MTAxNjEwMzk1NFowIjEgMB4GA1UEAxMXbXl2\r\n" \
+ "ZW5kb3IuY29tL3RjYXQvbXlkZXYwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQB\r\n" \
+ "aWwFDNj1bpQIdN+Kp2cHWw55U/+fa+OmZnoy1B4BOT+822jdwPBuyXWAQoBdYdQJ\r\n" \
+ "ff4RgmhczyV4PhArPIuAoxYwFDASBgkrBgEEAYLfKgMEBQABAQEBMAoGCCqGSM49\r\n" \
+ "BAMCA0cAMEQCIBEHxiEDij26y6V77Q311Gj4CZAuZuPGXZpnzL2BLk7bAiAlFk6G\r\n" \
+ "mYGzkcrYyssFI9HlPgrisWoMmgummaTtCuvrEw==\r\n" \
+ "-----END CERTIFICATE-----\r\n"
+
+#define OT_TCAT_PRIV_KEY \
+ "-----BEGIN EC PRIVATE KEY-----\r\n" \
+ "MHcCAQEEIDeJ6lVQKiOIBxKwTZp6TkU5QVHt9pvXOR9CGpPBI3DhoAoGCCqGSM49\r\n" \
+ "AwEHoUQDQgAEAWlsBQzY9W6UCHTfiqdnB1sOeVP/n2vjpmZ6MtQeATk/vNto3cDw\r\n" \
+ "bsl1gEKAXWHUCX3+EYJoXM8leD4QKzyLgA==\r\n" \
+ "-----END EC PRIVATE KEY-----\r\n"
+
+#define OT_TCAT_TRUSTED_ROOT_CERTIFICATE \
+ "-----BEGIN CERTIFICATE-----\r\n" \
+ "MIICCDCCAa2gAwIBAgIJAIKxygBXoH+5MAoGCCqGSM49BAMCMG8xCzAJBgNVBAYT\r\n" \
+ "AlhYMRAwDgYDVQQIEwdNeVN0YXRlMQ8wDQYDVQQHEwZNeUNpdHkxDzANBgNVBAsT\r\n" \
+ "Bk15VW5pdDERMA8GA1UEChMITXlWZW5kb3IxGTAXBgNVBAMTEHd3dy5teXZlbmRv\r\n" \
+ "ci5jb20wHhcNMjMxMDE2MTAzMzE1WhcNMjYxMDE2MTAzMzE1WjBvMQswCQYDVQQG\r\n" \
+ "EwJYWDEQMA4GA1UECBMHTXlTdGF0ZTEPMA0GA1UEBxMGTXlDaXR5MQ8wDQYDVQQL\r\n" \
+ "EwZNeVVuaXQxETAPBgNVBAoTCE15VmVuZG9yMRkwFwYDVQQDExB3d3cubXl2ZW5k\r\n" \
+ "b3IuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWdyzPAXGKeZY94OhHAWX\r\n" \
+ "HzJfQIjGSyaOzlgL9OEFw2SoUDncLKPGwfPAUSfuMyEkzszNDM0HHkBsDLqu4n25\r\n" \
+ "/6MyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU4EynoSw9eDKZEVPkums2\r\n" \
+ "IWLAJCowCgYIKoZIzj0EAwIDSQAwRgIhAMYGGL9xShyE6P9wEU+MAYF6W3CzdrwV\r\n" \
+ "kuerX1encIH2AiEA5rq490NUobM1Au43roxJq1T6Z43LscPVbGZfULD1Jq0=\r\n" \
+ "-----END CERTIFICATE-----\r\n"
+
+namespace ot {
+
+class TestBleSecure
+{
+public:
+ TestBleSecure(void)
+ : mIsConnected(false)
+ , mIsBleConnectionOpen(false)
+ {
+ }
+
+ void HandleBleSecureConnect(bool aConnected, bool aBleConnectionOpen)
+ {
+ mIsConnected = aConnected;
+ mIsBleConnectionOpen = aBleConnectionOpen;
+ }
+
+ bool IsConnected(void) const { return mIsConnected; }
+ bool IsBleConnectionOpen(void) const { return mIsBleConnectionOpen; }
+
+private:
+ bool mIsConnected;
+ bool mIsBleConnectionOpen;
+};
+
+static void HandleBleSecureConnect(otInstance *aInstance, bool aConnected, bool aBleConnectionOpen, void *aContext)
+{
+ OT_UNUSED_VARIABLE(aInstance);
+
+ static_cast<TestBleSecure *>(aContext)->HandleBleSecureConnect(aConnected, aBleConnectionOpen);
+}
+
+void TestTcat(void)
+{
+ const char kPskdVendor[] = "J01NM3";
+ const char kUrl[] = "dummy_url";
+ constexpr uint16_t kConnectionId = 0;
+
+ TestBleSecure ble;
+ Instance *instance = testInitInstance();
+
+ otTcatVendorInfo vendorInfo = {.mProvisioningUrl = kUrl, .mPskdString = kPskdVendor};
+
+ otBleSecureSetCertificate(instance, reinterpret_cast<const uint8_t *>(OT_TCAT_X509_CERT), sizeof(OT_TCAT_X509_CERT),
+ reinterpret_cast<const uint8_t *>(OT_TCAT_PRIV_KEY), sizeof(OT_TCAT_PRIV_KEY));
+
+ otBleSecureSetCaCertificateChain(instance, reinterpret_cast<const uint8_t *>(OT_TCAT_TRUSTED_ROOT_CERTIFICATE),
+ sizeof(OT_TCAT_TRUSTED_ROOT_CERTIFICATE));
+
+ otBleSecureSetSslAuthMode(instance, true);
+
+ // Validate BLE secure and Tcat start APIs
+ VerifyOrQuit(otBleSecureTcatStart(instance, &vendorInfo, nullptr) == kErrorInvalidState);
+ SuccessOrQuit(otBleSecureStart(instance, HandleBleSecureConnect, nullptr, true, &ble));
+ VerifyOrQuit(otBleSecureStart(instance, HandleBleSecureConnect, nullptr, true, nullptr) == kErrorAlready);
+ SuccessOrQuit(otBleSecureTcatStart(instance, &vendorInfo, nullptr));
+
+ // Validate connection callbacks when platform informs that peer has connected/disconnected
+ otPlatBleGapOnConnected(instance, kConnectionId);
+ VerifyOrQuit(!ble.IsConnected() && ble.IsBleConnectionOpen());
+ otPlatBleGapOnDisconnected(instance, kConnectionId);
+ VerifyOrQuit(!ble.IsConnected() && !ble.IsBleConnectionOpen());
+
+ // Validate connection callbacks when calling `otBleSecureDisconnect()`
+ otPlatBleGapOnConnected(instance, kConnectionId);
+ VerifyOrQuit(!ble.IsConnected() && ble.IsBleConnectionOpen());
+ otBleSecureDisconnect(instance);
+ VerifyOrQuit(!ble.IsConnected() && !ble.IsBleConnectionOpen());
+
+ // Validate TLS connection can be started only when peer is connected
+ otPlatBleGapOnConnected(instance, kConnectionId);
+ SuccessOrQuit(otBleSecureConnect(instance));
+ otBleSecureDisconnect(instance);
+ VerifyOrQuit(otBleSecureConnect(instance) == kErrorInvalidState);
+
+ // Validate Tcat state changes after stopping BLE secure
+ VerifyOrQuit(otBleSecureIsTcatEnabled(instance));
+ otBleSecureStop(instance);
+ VerifyOrQuit(!otBleSecureIsTcatEnabled(instance));
+
+ testFreeInstance(instance);
+}
+
+} // namespace ot
+
+#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+
+int main(void)
+{
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+ ot::TestTcat();
+ printf("All tests passed\n");
+#else
+ printf("Tcat is not enabled\n");
+ return -1;
+#endif
+ return 0;
+}
diff --git a/third_party/mbedtls/mbedtls-config.h b/third_party/mbedtls/mbedtls-config.h
index a3e06ac..29aa49e 100644
--- a/third_party/mbedtls/mbedtls-config.h
+++ b/third_party/mbedtls/mbedtls-config.h
@@ -94,6 +94,7 @@
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
#define MBEDTLS_SSL_KEEP_PEER_CERTIFICATE
+#define MBEDTLS_GCM_C
#endif
#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
@@ -132,7 +133,9 @@
#define MBEDTLS_MEMORY_BUFFER_ALLOC_C
#endif
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
+#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
+#define MBEDTLS_SSL_MAX_CONTENT_LEN 2000 /**< Maxium fragment length in bytes */
+#elif OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
#define MBEDTLS_SSL_MAX_CONTENT_LEN 900 /**< Maxium fragment length in bytes */
#else
#define MBEDTLS_SSL_MAX_CONTENT_LEN 768 /**< Maxium fragment length in bytes */
diff --git a/tools/tcat_ble_client/bbtc.py b/tools/tcat_ble_client/bbtc.py
old mode 100644
new mode 100755
index fa2ebbb..92539c1
--- a/tools/tcat_ble_client/bbtc.py
+++ b/tools/tcat_ble_client/bbtc.py
@@ -34,6 +34,7 @@
from ble.ble_connection_constants import BBTC_SERVICE_UUID, BBTC_TX_CHAR_UUID, \
BBTC_RX_CHAR_UUID, SERVER_COMMON_NAME
from ble.ble_stream import BleStream
+from ble.udp_stream import UdpStream
from ble.ble_stream_secure import BleStreamSecure
from ble import ble_scanner
from cli.cli import CLI
@@ -47,10 +48,12 @@
parser = argparse.ArgumentParser(description='Device parameters')
parser.add_argument('--debug', help='Enable debug logs', action='store_true')
+ parser.add_argument('--cert_path', help='Path to certificate chain and key', action='store', default='auth')
group = parser.add_mutually_exclusive_group()
group.add_argument('--mac', type=str, help='Device MAC address', action='store')
group.add_argument('--name', type=str, help='Device name', action='store')
group.add_argument('--scan', help='Scan all available devices', action='store_true')
+ group.add_argument('--simulation', help='Connect to simulation node id', action='store')
args = parser.parse_args()
if args.debug:
@@ -63,12 +66,11 @@
if not (device is None):
print(f'Connecting to {device}')
- ble_stream = await BleStream.create(device.address, BBTC_SERVICE_UUID, BBTC_TX_CHAR_UUID, BBTC_RX_CHAR_UUID)
- ble_sstream = BleStreamSecure(ble_stream)
+ ble_sstream = BleStreamSecure(device)
ble_sstream.load_cert(
- certfile=path.join('auth', 'commissioner_cert.pem'),
- keyfile=path.join('auth', 'commissioner_key.pem'),
- cafile=path.join('auth', 'ca_cert.pem'),
+ certfile=path.join(args.cert_path, 'commissioner_cert.pem'),
+ keyfile=path.join(args.cert_path, 'commissioner_key.pem'),
+ cafile=path.join(args.cert_path, 'ca_cert.pem'),
)
print('Setting up secure channel...')
@@ -96,11 +98,16 @@
device = None
if args.mac:
device = await ble_scanner.find_first_by_mac(args.mac)
+ device = await BleStream.create(device.address, BBTC_SERVICE_UUID, BBTC_TX_CHAR_UUID, BBTC_RX_CHAR_UUID)
elif args.name:
device = await ble_scanner.find_first_by_name(args.name)
+ device = await BleStream.create(device.address, BBTC_SERVICE_UUID, BBTC_TX_CHAR_UUID, BBTC_RX_CHAR_UUID)
elif args.scan:
tcat_devices = await ble_scanner.scan_tcat_devices()
device = select_device_by_user_input(tcat_devices)
+ device = await BleStream.create(device.address, BBTC_SERVICE_UUID, BBTC_TX_CHAR_UUID, BBTC_RX_CHAR_UUID)
+ elif args.simulation:
+ device = UdpStream("127.0.0.1", int(args.simulation))
return device
diff --git a/tools/tcat_ble_client/ble/ble_stream_secure.py b/tools/tcat_ble_client/ble/ble_stream_secure.py
index 9d15b79..4731c18 100644
--- a/tools/tcat_ble_client/ble/ble_stream_secure.py
+++ b/tools/tcat_ble_client/ble/ble_stream_secure.py
@@ -30,15 +30,13 @@
import ssl
import logging
-from .ble_stream import BleStream
-
logger = logging.getLogger(__name__)
class BleStreamSecure:
- def __init__(self, ble_stream: BleStream):
- self.ble_stream = ble_stream
+ def __init__(self, stream):
+ self.stream = stream
self.ssl_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
self.incoming = ssl.MemoryBIO()
self.outgoing = ssl.MemoryBIO()
@@ -67,12 +65,12 @@
# SSLWantWrite means ssl wants to send data over the link,
# but might need a receive first
except ssl.SSLWantWriteError:
- output = await self.ble_stream.recv(4096)
+ output = await self.stream.recv(4096)
if output:
self.incoming.write(output)
data = self.outgoing.read()
if data:
- await self.ble_stream.send(data)
+ await self.stream.send(data)
await asyncio.sleep(0.1)
# SSLWantRead means ssl wants to receive data from the link,
@@ -80,8 +78,8 @@
except ssl.SSLWantReadError:
data = self.outgoing.read()
if data:
- await self.ble_stream.send(data)
- output = await self.ble_stream.recv(4096)
+ await self.stream.send(data)
+ output = await self.stream.recv(4096)
if output:
self.incoming.write(output)
await asyncio.sleep(0.1)
@@ -89,14 +87,14 @@
async def send(self, bytes):
self.ssl_object.write(bytes)
encode = self.outgoing.read(4096)
- await self.ble_stream.send(encode)
+ await self.stream.send(encode)
async def recv(self, buffersize, timeout=1):
end_time = asyncio.get_event_loop().time() + timeout
- data = await self.ble_stream.recv(buffersize)
+ data = await self.stream.recv(buffersize)
while not data and asyncio.get_event_loop().time() < end_time:
await asyncio.sleep(0.1)
- data = await self.ble_stream.recv(buffersize)
+ data = await self.stream.recv(buffersize)
if not data:
logger.warning('No response when response expected.')
return b''
@@ -108,10 +106,10 @@
break
# if recv called before entire message was received from the link
except ssl.SSLWantReadError:
- more = await self.ble_stream.recv(buffersize)
+ more = await self.stream.recv(buffersize)
while not more:
await asyncio.sleep(0.1)
- more = await self.ble_stream.recv(buffersize)
+ more = await self.stream.recv(buffersize)
self.incoming.write(more)
return decode
diff --git a/tools/tcat_ble_client/ble/udp_stream.py b/tools/tcat_ble_client/ble/udp_stream.py
new file mode 100644
index 0000000..5421940
--- /dev/null
+++ b/tools/tcat_ble_client/ble/udp_stream.py
@@ -0,0 +1,56 @@
+"""
+ 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 itertools import count, takewhile
+from typing import Iterator
+import logging
+import time
+from asyncio import sleep
+import socket
+
+logger = logging.getLogger(__name__)
+
+
+class UdpStream:
+ BASE_PORT = 10000
+
+ def __init__(self, address, node_id):
+ self.__receive_buffer = b''
+ self.__last_recv_time = None
+ self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self.address = (address, self.BASE_PORT + node_id)
+
+ async def send(self, data):
+ logger.debug(f'sending {data}')
+ self.socket.sendto(data, self.address)
+ return len(data)
+
+ async def recv(self, bufsize):
+ message = self.socket.recv(bufsize)
+ logger.debug(f'retrieved {message}')
+ return message