aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHanda Wang <handaw@google.com>2024-04-08 03:09:57 +0000
committerHanda Wang <handaw@google.com>2024-04-08 03:10:53 +0000
commit7d6458212a36158d09722a3cd7382fbd232dcc02 (patch)
treeb24eadb4f6a55c07c14cf5c6dbbea583f2c6c37a
parenta4a9439bdc17795c431c16002fa6c1274f336ce6 (diff)
parenta5c17b77635bb43d02d4dec96fbf4b7eeb43be06 (diff)
downloadopenthread-7d6458212a36158d09722a3cd7382fbd232dcc02.tar.gz
[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
-rw-r--r--.github/workflows/build.yml30
-rw-r--r--.github/workflows/codeql.yml6
-rw-r--r--.github/workflows/docker.yml4
-rw-r--r--.github/workflows/fuzz.yml2
-rw-r--r--.github/workflows/makefile-check.yml2
-rw-r--r--.github/workflows/otbr.yml22
-rw-r--r--.github/workflows/otci.yml2
-rw-r--r--.github/workflows/otns.yml22
-rw-r--r--.github/workflows/posix.yml35
-rw-r--r--.github/workflows/scorecards.yml6
-rw-r--r--.github/workflows/simulation-1.1.yml47
-rw-r--r--.github/workflows/simulation-1.2.yml93
-rw-r--r--.github/workflows/size.yml2
-rw-r--r--.github/workflows/toranj.yml36
-rw-r--r--.github/workflows/unit.yml12
-rw-r--r--.github/workflows/version.yml2
-rw-r--r--doc/ot_config_doc.h10
-rw-r--r--etc/cmake/options.cmake5
-rw-r--r--examples/config/ot-core-config-check-size-br.h1
-rw-r--r--examples/config/ot-core-config-check-size-ftd.h1
-rw-r--r--examples/config/ot-core-config-check-size-mtd.h1
-rw-r--r--examples/platforms/simulation/CMakeLists.txt1
-rw-r--r--examples/platforms/simulation/ble.c157
-rw-r--r--examples/platforms/simulation/infra_if.c297
-rw-r--r--examples/platforms/simulation/platform-simulation.h65
-rw-r--r--examples/platforms/simulation/radio.c182
-rw-r--r--examples/platforms/simulation/simul_utils.c228
-rw-r--r--examples/platforms/simulation/simul_utils.h151
-rw-r--r--examples/platforms/simulation/system.c87
-rw-r--r--examples/platforms/simulation/trel.c149
-rw-r--r--examples/platforms/simulation/uart.c32
-rw-r--r--examples/platforms/utils/mac_frame.cpp2
-rw-r--r--include/openthread/border_agent.h134
-rw-r--r--include/openthread/border_routing.h16
-rw-r--r--include/openthread/channel_manager.h86
-rw-r--r--include/openthread/instance.h2
-rw-r--r--include/openthread/ip6.h1
-rw-r--r--include/openthread/link.h21
-rw-r--r--include/openthread/message.h35
-rw-r--r--include/openthread/thread.h4
-rwxr-xr-xscript/cmake-build1
-rwxr-xr-xscript/make-pretty1
-rw-r--r--src/cli/BUILD.gn4
-rw-r--r--src/cli/CMakeLists.txt2
-rw-r--r--src/cli/README.md173
-rw-r--r--src/cli/README_BR.md23
-rw-r--r--src/cli/README_DATASET.md124
-rw-r--r--src/cli/README_NETDATA.md17
-rw-r--r--src/cli/cli.cpp297
-rw-r--r--src/cli/cli.hpp152
-rw-r--r--src/cli/cli_bbr.cpp3
-rw-r--r--src/cli/cli_bbr.hpp8
-rw-r--r--src/cli/cli_br.cpp43
-rw-r--r--src/cli/cli_br.hpp8
-rw-r--r--src/cli/cli_coap.cpp4
-rw-r--r--src/cli/cli_coap.hpp6
-rw-r--r--src/cli/cli_coap_secure.cpp21
-rw-r--r--src/cli/cli_coap_secure.hpp6
-rw-r--r--src/cli/cli_commissioner.hpp8
-rw-r--r--src/cli/cli_dataset.cpp45
-rw-r--r--src/cli/cli_dataset.hpp8
-rw-r--r--src/cli/cli_dns.cpp3
-rw-r--r--src/cli/cli_dns.hpp8
-rw-r--r--src/cli/cli_history.hpp8
-rw-r--r--src/cli/cli_joiner.hpp8
-rw-r--r--src/cli/cli_link_metrics.cpp4
-rw-r--r--src/cli/cli_link_metrics.hpp6
-rw-r--r--src/cli/cli_mac_filter.hpp8
-rw-r--r--src/cli/cli_network_data.cpp175
-rw-r--r--src/cli/cli_network_data.hpp14
-rw-r--r--src/cli/cli_ping.cpp4
-rw-r--r--src/cli/cli_ping.hpp6
-rw-r--r--src/cli/cli_srp_client.cpp19
-rw-r--r--src/cli/cli_srp_client.hpp6
-rw-r--r--src/cli/cli_srp_server.cpp3
-rw-r--r--src/cli/cli_srp_server.hpp8
-rw-r--r--src/cli/cli_tcat.cpp2
-rw-r--r--src/cli/cli_tcat.hpp8
-rw-r--r--src/cli/cli_tcp.cpp2
-rw-r--r--src/cli/cli_tcp.hpp6
-rw-r--r--src/cli/cli_udp.cpp4
-rw-r--r--src/cli/cli_udp.hpp6
-rw-r--r--src/cli/cli_utils.cpp (renamed from src/cli/cli_output.cpp)146
-rw-r--r--src/cli/cli_utils.hpp (renamed from src/cli/cli_output.hpp)154
-rw-r--r--src/cli/radio.cmake2
-rw-r--r--src/core/BUILD.gn2
-rw-r--r--src/core/api/border_agent_api.cpp31
-rw-r--r--src/core/api/border_routing_api.cpp5
-rw-r--r--src/core/api/channel_manager_api.cpp35
-rw-r--r--src/core/api/message_api.cpp5
-rw-r--r--src/core/api/thread_api.cpp6
-rw-r--r--src/core/backbone_router/bbr_manager.cpp3
-rw-r--r--src/core/border_router/routing_manager.cpp377
-rw-r--r--src/core/border_router/routing_manager.hpp116
-rw-r--r--src/core/coap/coap.cpp6
-rw-r--r--src/core/coap/coap_message.cpp31
-rw-r--r--src/core/coap/coap_message.hpp29
-rw-r--r--src/core/common/log.cpp10
-rw-r--r--src/core/common/log.hpp20
-rw-r--r--src/core/common/message.cpp45
-rw-r--r--src/core/common/message.hpp55
-rw-r--r--src/core/common/string.cpp7
-rw-r--r--src/core/common/string.hpp4
-rw-r--r--src/core/common/tasklet.hpp2
-rw-r--r--src/core/config/border_agent.h10
-rw-r--r--src/core/config/channel_manager.h12
-rw-r--r--src/core/config/misc.h19
-rw-r--r--src/core/config/mle.h4
-rw-r--r--src/core/instance/instance.cpp4
-rw-r--r--src/core/instance/instance.hpp8
-rw-r--r--src/core/mac/mac.cpp8
-rw-r--r--src/core/mac/mac_frame.cpp6
-rw-r--r--src/core/meshcop/border_agent.cpp182
-rw-r--r--src/core/meshcop/border_agent.hpp132
-rw-r--r--src/core/meshcop/commissioner.cpp13
-rw-r--r--src/core/meshcop/dataset.cpp12
-rw-r--r--src/core/meshcop/dataset.hpp14
-rw-r--r--src/core/meshcop/dataset_local.cpp10
-rw-r--r--src/core/meshcop/dataset_local.hpp8
-rw-r--r--src/core/meshcop/dataset_manager.cpp14
-rw-r--r--src/core/meshcop/dataset_manager.hpp16
-rw-r--r--src/core/meshcop/joiner.cpp6
-rw-r--r--src/core/meshcop/joiner_router.cpp2
-rw-r--r--src/core/meshcop/meshcop.cpp10
-rw-r--r--src/core/meshcop/meshcop.hpp16
-rw-r--r--src/core/meshcop/meshcop_leader.cpp6
-rw-r--r--src/core/meshcop/tcat_agent.cpp18
-rw-r--r--src/core/meshcop/tcat_agent.hpp6
-rw-r--r--src/core/net/dhcp6_client.cpp2
-rw-r--r--src/core/net/dhcp6_server.cpp7
-rw-r--r--src/core/net/dnssd_server.cpp2
-rw-r--r--src/core/net/ip6.cpp43
-rw-r--r--src/core/net/ip6.hpp7
-rw-r--r--src/core/net/nd6.cpp57
-rw-r--r--src/core/net/nd6.hpp260
-rw-r--r--src/core/net/sntp_client.cpp2
-rw-r--r--src/core/net/socket.hpp24
-rw-r--r--src/core/net/srp_server.cpp147
-rw-r--r--src/core/net/srp_server.hpp5
-rw-r--r--src/core/openthread-core-config.h2
-rw-r--r--src/core/radio/ble_secure.cpp27
-rw-r--r--src/core/radio/radio.hpp7
-rw-r--r--src/core/radio/radio_platform.cpp4
-rw-r--r--src/core/thread/address_resolver.hpp5
-rw-r--r--src/core/thread/csl_tx_scheduler.hpp2
-rw-r--r--src/core/thread/discover_scanner.cpp11
-rw-r--r--src/core/thread/dua_manager.cpp4
-rw-r--r--src/core/thread/energy_scan_server.cpp2
-rw-r--r--src/core/thread/key_manager.cpp77
-rw-r--r--src/core/thread/key_manager.hpp60
-rw-r--r--src/core/thread/link_quality.cpp11
-rw-r--r--src/core/thread/mesh_forwarder.cpp14
-rw-r--r--src/core/thread/mesh_forwarder.hpp2
-rw-r--r--src/core/thread/mesh_forwarder_ftd.cpp18
-rw-r--r--src/core/thread/mle.cpp215
-rw-r--r--src/core/thread/mle.hpp11
-rw-r--r--src/core/thread/mle_router.cpp317
-rw-r--r--src/core/thread/mle_router.hpp11
-rw-r--r--src/core/thread/network_data.cpp504
-rw-r--r--src/core/thread/network_data.hpp44
-rw-r--r--src/core/thread/network_data_leader_ftd.cpp20
-rw-r--r--src/core/thread/network_data_notifier.cpp9
-rw-r--r--src/core/thread/network_data_publisher.cpp100
-rw-r--r--src/core/thread/network_data_publisher.hpp5
-rw-r--r--src/core/thread/network_data_types.hpp33
-rw-r--r--src/core/thread/panid_query_server.cpp2
-rw-r--r--src/core/thread/version.hpp8
-rw-r--r--src/core/utils/channel_manager.cpp178
-rw-r--r--src/core/utils/channel_manager.hpp116
-rw-r--r--src/core/utils/otns.cpp16
-rw-r--r--src/lib/platform/reset_util.h4
-rw-r--r--src/lib/spinel/BUILD.gn2
-rw-r--r--src/lib/spinel/CMakeLists.txt6
-rw-r--r--src/lib/spinel/logger.cpp748
-rw-r--r--src/lib/spinel/logger.hpp69
-rw-r--r--src/lib/spinel/multi_frame_buffer.hpp2
-rw-r--r--src/lib/spinel/radio_spinel.cpp705
-rw-r--r--src/lib/spinel/radio_spinel.hpp17
-rw-r--r--src/lib/spinel/spinel_encoder.cpp2
-rw-r--r--src/lib/spinel/spinel_encoder.hpp6
-rw-r--r--src/ncp/ncp_base.cpp2
-rw-r--r--src/ncp/ncp_base_mtd.cpp2
-rw-r--r--src/posix/platform/config_file.hpp6
-rw-r--r--src/posix/platform/configuration.cpp22
-rw-r--r--src/posix/platform/configuration.hpp12
-rw-r--r--src/posix/platform/daemon.cpp14
-rw-r--r--src/posix/platform/daemon.hpp8
-rw-r--r--src/posix/platform/firewall.cpp2
-rw-r--r--src/posix/platform/hdlc_interface.cpp8
-rw-r--r--src/posix/platform/hdlc_interface.hpp12
-rw-r--r--src/posix/platform/infra_if.cpp64
-rw-r--r--src/posix/platform/infra_if.hpp28
-rw-r--r--src/posix/platform/ip6_utils.hpp69
-rw-r--r--src/posix/platform/logger.hpp141
-rw-r--r--src/posix/platform/mainloop.hpp2
-rw-r--r--src/posix/platform/multicast_routing.cpp69
-rw-r--r--src/posix/platform/multicast_routing.hpp7
-rw-r--r--src/posix/platform/netif.cpp296
-rw-r--r--src/posix/platform/openthread-posix-config.h6
-rw-r--r--src/posix/platform/platform-posix.h6
-rw-r--r--src/posix/platform/power.hpp6
-rw-r--r--src/posix/platform/radio.cpp12
-rw-r--r--src/posix/platform/radio.hpp19
-rw-r--r--src/posix/platform/radio_url.hpp6
-rw-r--r--src/posix/platform/resolver.cpp17
-rw-r--r--src/posix/platform/resolver.hpp20
-rw-r--r--src/posix/platform/settings.hpp6
-rw-r--r--src/posix/platform/spi_interface.cpp68
-rw-r--r--src/posix/platform/spi_interface.hpp11
-rw-r--r--src/posix/platform/trel.cpp83
-rw-r--r--src/posix/platform/udp.cpp51
-rw-r--r--src/posix/platform/udp.hpp8
-rw-r--r--src/posix/platform/vendor_interface.hpp6
-rwxr-xr-xtests/scripts/expect/cli-tcat.exp61
-rwxr-xr-xtests/scripts/expect/posix-rcp-local-host.exp44
-rwxr-xr-xtests/scripts/thread-cert/addon_test_channel_manager_autocsl.py143
-rwxr-xr-xtests/scripts/thread-cert/border_router/test_advertising_proxy.py8
-rwxr-xr-xtests/scripts/thread-cert/border_router/test_manual_maddress.py5
-rwxr-xr-xtests/scripts/thread-cert/node.py110
-rwxr-xr-xtests/scripts/thread-cert/test_detach.py4
-rwxr-xr-xtests/scripts/thread-cert/test_key_rotation_and_key_guard_time.py171
-rwxr-xr-xtests/scripts/thread-cert/test_mle_msg_key_seq_jump.py13
-rwxr-xr-xtests/scripts/thread-cert/test_netdata_publisher.py86
-rwxr-xr-xtests/scripts/thread-cert/test_srp_server_reboot_port.py4
-rwxr-xr-xtests/scripts/thread-cert/v1_2_router_5_1_1.py6
-rwxr-xr-xtests/scripts/thread-cert/v1_2_test_backbone_router_service.py8
-rwxr-xr-xtests/scripts/thread-cert/v1_2_test_domain_unicast_address.py12
-rwxr-xr-xtests/scripts/thread-cert/v1_2_test_domain_unicast_address_registration.py2
-rwxr-xr-xtests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py4
-rwxr-xr-xtests/scripts/thread-cert/v1_2_test_parent_selection.py54
-rwxr-xr-xtests/toranj/build.sh32
-rw-r--r--tests/toranj/cli/cli.py71
-rwxr-xr-xtests/toranj/cli/test-011-network-data-timeout.py26
-rwxr-xr-xtests/toranj/cli/test-020-net-diag-vendor-info.py4
-rwxr-xr-xtests/toranj/cli/test-028-border-agent-ephemeral-key.py96
-rwxr-xr-xtests/toranj/cli/test-400-srp-client-server.py16
-rwxr-xr-xtests/toranj/cli/test-401-srp-server-address-cache-snoop.py213
-rwxr-xr-xtests/toranj/cli/test-500-two-brs-two-networks.py111
-rwxr-xr-xtests/toranj/cli/test-501-multi-br-failure-recovery.py210
-rwxr-xr-xtests/toranj/cli/test-502-multi-br-leader-failure-recovery.py210
-rwxr-xr-xtests/toranj/cli/test-602-channel-manager-channel-select.py67
-rw-r--r--tests/toranj/openthread-core-toranj-config-posix.h10
-rw-r--r--tests/toranj/openthread-core-toranj-config-simulation.h4
-rw-r--r--tests/toranj/openthread-core-toranj-config.h14
-rwxr-xr-xtests/toranj/start.sh5
-rw-r--r--tests/unit/CMakeLists.txt21
-rw-r--r--tests/unit/test_network_data.cpp142
-rw-r--r--tests/unit/test_platform.cpp14
-rw-r--r--tests/unit/test_pskc.cpp110
-rw-r--r--tests/unit/test_routing_manager.cpp184
-rw-r--r--tests/unit/test_tcat.cpp170
-rw-r--r--third_party/mbedtls/mbedtls-config.h5
-rwxr-xr-x[-rw-r--r--]tools/tcat_ble_client/bbtc.py17
-rw-r--r--tools/tcat_ble_client/ble/ble_stream_secure.py24
-rw-r--r--tools/tcat_ble_client/ble/udp_stream.py56
255 files changed, 8623 insertions, 3865 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 007430e32..953788ee4 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -53,7 +53,7 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 158892fe4..cc1f543dc 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -59,14 +59,14 @@ jobs:
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 @@ jobs:
./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 c336a2ba9..9738b850c 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -59,7 +59,7 @@ jobs:
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 @@ jobs:
${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 97cb4b53f..da612bb36 100644
--- a/.github/workflows/fuzz.yml
+++ b/.github/workflows/fuzz.yml
@@ -61,7 +61,7 @@ jobs:
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 83bce98ca..1d7fa5518 100644
--- a/.github/workflows/makefile-check.yml
+++ b/.github/workflows/makefile-check.yml
@@ -52,7 +52,7 @@ jobs:
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 4ba7ea6c3..ac0474435 100644
--- a/.github/workflows/otbr.yml
+++ b/.github/workflows/otbr.yml
@@ -62,7 +62,7 @@ jobs:
# 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 @@ jobs:
export CI_ENV="$(bash <(curl -s https://codecov.io/env)) -e GITHUB_ACTIONS -e COVERAGE"
echo "CI_ENV=${CI_ENV}"
sudo -E ./script/test cert_suite ./tests/scripts/thread-cert/backbone/*.py || (sudo chmod a+r *.log *.json *.pcap && false)
- - uses: actions/upload-artifact@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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
export CI_ENV="$(bash <(curl -s https://codecov.io/env)) -e GITHUB_ACTIONS -e COVERAGE"
echo "CI_ENV=${CI_ENV}"
sudo -E ./script/test cert_suite ${{ matrix.cert_scripts }} || (sudo chmod a+r *.log *.json *.pcap && false)
- - uses: actions/upload-artifact@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 @@ jobs:
- 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 @@ jobs:
- 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 @@ jobs:
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 37a014914..d22c227b9 100644
--- a/.github/workflows/otci.yml
+++ b/.github/workflows/otci.yml
@@ -61,7 +61,7 @@ jobs:
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 4b46095d8..7abbc9120 100644
--- a/.github/workflows/otns.yml
+++ b/.github/workflows/otns.yml
@@ -62,7 +62,7 @@ jobs:
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 @@ jobs:
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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
- 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 @@ jobs:
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 7ab0ef2c7..fd8e7d899 100644
--- a/.github/workflows/posix.yml
+++ b/.github/workflows/posix.yml
@@ -56,10 +56,11 @@ jobs:
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 @@ jobs:
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 @@ jobs:
- 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 @@ jobs:
CRASHED=$(./script/test check_crash | tail -1)
[[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
echo "CRASHED_TUN=$CRASHED" >> $GITHUB_ENV
- - uses: actions/upload-artifact@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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
- 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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
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 5fd261bfa..a9a13ccf8 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -60,7 +60,7 @@ jobs:
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 @@ jobs:
# 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 @@ jobs:
# 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 9f64444c2..ee08f3954 100644
--- a/.github/workflows/simulation-1.1.yml
+++ b/.github/workflows/simulation-1.1.yml
@@ -59,7 +59,7 @@ jobs:
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 @@ jobs:
- 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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
- 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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
- 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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
- 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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
CRASHED=$(./script/test check_crash | tail -1)
[[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
echo "CRASHED_CLI=$CRASHED" >> $GITHUB_ENV
- - uses: actions/upload-artifact@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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
- 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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
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 35a0f0d36..26153f447 100644
--- a/.github/workflows/simulation-1.2.yml
+++ b/.github/workflows/simulation-1.2.yml
@@ -70,7 +70,7 @@ jobs:
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 @@ jobs:
CRASHED=$(./script/test check_crash | tail -1)
[[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
echo "CRASHED=$CRASHED" >> $GITHUB_ENV
- - uses: actions/upload-artifact@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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
CRASHED=$(./script/test check_crash | tail -1)
[[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
echo "CRASHED=$CRASHED" >> $GITHUB_ENV
- - uses: actions/upload-artifact@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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
- 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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
CRASHED=$(./script/test check_crash | tail -1)
[[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
echo "CRASHED=$CRASHED" >> $GITHUB_ENV
- - uses: actions/upload-artifact@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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
CRASHED=$(./script/test check_crash | tail -1)
[[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed."
echo "CRASHED=$CRASHED" >> $GITHUB_ENV
- - uses: actions/upload-artifact@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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
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 7b0358b64..98de9d756 100644
--- a/.github/workflows/size.yml
+++ b/.github/workflows/size.yml
@@ -53,7 +53,7 @@ jobs:
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 29f2d8b21..6b79b13c5 100644
--- a/.github/workflows/toranj.yml
+++ b/.github/workflows/toranj.yml
@@ -63,7 +63,7 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 @@ jobs:
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 672aac37c..bf685cac7 100644
--- a/.github/workflows/unit.yml
+++ b/.github/workflows/unit.yml
@@ -53,7 +53,7 @@ jobs:
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 @@ jobs:
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 @@ jobs:
- 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 @@ jobs:
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 @@ jobs:
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 5b6d3e79c..16dc6370b 100644
--- a/.github/workflows/version.yml
+++ b/.github/workflows/version.yml
@@ -49,7 +49,7 @@ jobs:
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 564950bce..87af22965 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 bad9193d7..da77ef01f 100644
--- a/etc/cmake/options.cmake
+++ b/etc/cmake/options.cmake
@@ -180,6 +180,7 @@ ot_option(OT_BORDER_ROUTING OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE "border rout
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 @@ if(ot_index EQUAL -1)
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 @@ if(ot_index EQUAL -1)
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 8310019f7..e01bc9ca8 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 63e7ad484..bdd69d055 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 88b2c1198..d5ca74cbc 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 41a1dbff6..12580e2d1 100644
--- a/examples/platforms/simulation/CMakeLists.txt
+++ b/examples/platforms/simulation/CMakeLists.txt
@@ -72,6 +72,7 @@ add_library(openthread-simulation
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 2fe3c6453..d48922ac9 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 9596406d9..ccb5e9be7 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 @@ otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex,
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 @@ otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex)
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 0592b1429..d4407686a 100644
--- a/examples/platforms/simulation/platform-simulation.h
+++ b/examples/platforms/simulation/platform-simulation.h
@@ -159,7 +159,7 @@ void platformRadioDeinit(void);
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 @@ void platformTrelProcess(otInstance *aInstance, const fd_set *aReadFdSet, const
#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 03808b5af..960d8c66c 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 int sSockFd;
extern uint16_t sPortBase;
extern uint16_t sPortOffset;
#else
-static int sTxFd = -1;
-static int sRxFd = -1;
-static uint16_t sPortBase = 9000;
-static uint16_t sPortOffset = 0;
-static uint16_t sPort = 0;
+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 @@ static bool NodeIdFilterIsConnectable(uint16_t aNodeId)
{
bool isConnectable = true;
+ otEXPECT_ACTION(aNodeId != gNodeId, isConnectable = false);
+
switch (sFilterMode)
{
case kFilterOff:
@@ -202,6 +200,7 @@ static bool NodeIdFilterIsConnectable(uint16_t aNodeId)
break;
}
+exit:
return isConnectable;
}
@@ -407,82 +406,15 @@ void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
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 @@ void platformRadioReceive(otInstance *aInstance, uint8_t *aBuf, uint16_t aBufLen
#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 @@ void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct ti
}
// 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 @@ void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const
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 platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const
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 000000000..36b69af5d
--- /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 000000000..0d70f2dd3
--- /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 535cb1cac..2abe892e3 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 @@ enum
{
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 @@ static void PrintUsage(const char *aProgramName, int aExitCode)
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 @@ void otSysInit(int aArgCount, char *aArgVector[])
{"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 @@ void otSysInit(int aArgCount, char *aArgVector[])
};
#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 @@ void otSysInit(int aArgCount, char *aArgVector[])
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 @@ void otSysInit(int aArgCount, char *aArgVector[])
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
platformTrelInit(speedUpFactor);
#endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+ platformInfraIfInit();
+#endif
platformRandomInit();
}
@@ -200,6 +267,9 @@ void otSysDeinit(void)
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
platformTrelDeinit();
#endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+ // platformInfrIfDeinit();
+#endif
platformLoggingDeinit();
}
@@ -222,6 +292,13 @@ void otSysProcessDrivers(otInstance *aInstance)
#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 @@ void otSysProcessDrivers(otInstance *aInstance)
{
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 @@ void otSysProcessDrivers(otInstance *aInstance)
#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 cfc34ac9b..696b1a59c 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 @@ typedef struct Message
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 @@ static const char *messageTypeToString(MessageType aType)
}
#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 uint16_t getMessageSize(const Message *aMessage)
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 @@ static void processMessage(otInstance *aInstance, Message *aMessage, uint16_t aL
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 @@ void otPlatTrelEnable(otInstance *aInstance, uint16_t *aUdpPort)
{
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 @@ void platformTrelInit(uint32_t aSpeedUpFactor)
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)
- {
- FD_SET(sRxFd, aReadFdSet);
-
- if (aMaxFd != NULL && *aMaxFd < sRxFd)
- {
- *aMaxFd = sRxFd;
- }
- }
+ utilsAddSocketRxFd(&sSocket, aReadFdSet, aMaxFd);
- if ((aWriteFdSet != NULL) && (sNumPendingTx > 0))
+ if (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 fe3fcebe8..70d387118 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 @@ exit:
void platformUartUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int *aMaxFd)
{
- if (aReadFdSet != NULL)
- {
- 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;
- }
- }
+ utilsAddFdToFdSet(s_in_fd, aReadFdSet, aMaxFd);
+ utilsAddFdToFdSet(s_in_fd, aErrorFdSet, aMaxFd);
- if ((aWriteFdSet != NULL) && (s_write_length > 0))
+ if ((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 0ae88db34..36a1c975c 100644
--- a/examples/platforms/utils/mac_frame.cpp
+++ b/examples/platforms/utils/mac_frame.cpp
@@ -43,7 +43,7 @@ bool otMacFrameDoesAddrMatch(const otRadioFrame *aFrame,
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 1c7a53537..33578e77a 100644
--- a/include/openthread/border_agent.h
+++ b/include/openthread/border_agent.h
@@ -58,6 +58,30 @@ extern "C" {
#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 @@ uint16_t otBorderAgentGetUdpPort(otInstance *aInstance);
/**
* 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 @@ otError otBorderAgentGetId(otInstance *aInstance, otBorderAgentId *aId);
/**
* 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 otBorderAgentGetId(otInstance *aInstance, otBorderAgentId *aId);
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 afd2d7e5f..1e5ff6144 100644
--- a/include/openthread/border_routing.h
+++ b/include/openthread/border_routing.h
@@ -240,6 +240,22 @@ void otBorderRoutingSetRouteInfoOptionPreference(otInstance *aInstance, otRouteP
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 6afe3a4aa..e024957e6 100644
--- a/include/openthread/channel_manager.h
+++ b/include/openthread/channel_manager.h
@@ -47,8 +47,14 @@ extern "C" {
* @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 @@ void otChannelManagerRequestChannelChange(otInstance *aInstance, uint8_t aChanne
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 @@ uint8_t otChannelManagerGetRequestedChannel(otInstance *aInstance);
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 @@ otError otChannelManagerSetDelay(otInstance *aInstance, uint16_t aDelay);
*
* 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 otChannelManagerSetDelay(otInstance *aInstance, uint16_t aDelay);
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 @@ otError otChannelManagerRequestChannelSelect(otInstance *aInstance, bool aSkipQu
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 @@ void otChannelManagerSetAutoChannelSelectionEnabled(otInstance *aInstance, bool
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 546b5d17d..1ce766c02 100644
--- a/include/openthread/instance.h
+++ b/include/openthread/instance.h
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
-#define OPENTHREAD_API_VERSION (397)
+#define OPENTHREAD_API_VERSION (402)
/**
* @addtogroup api-instance
diff --git a/include/openthread/ip6.h b/include/openthread/ip6.h
index 7f54b7376..2636702b4 100644
--- a/include/openthread/ip6.h
+++ b/include/openthread/ip6.h
@@ -236,7 +236,6 @@ typedef struct otMessageInfo
otIp6Address mPeerAddr; ///< The peer IPv6 address.
uint16_t mSockPort; ///< The local transport-layer port.
uint16_t mPeerPort; ///< The peer transport-layer port.
- const void *mLinkInfo; ///< A pointer to link-specific information.
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 f4d203e72..15bc0d860 100644
--- a/include/openthread/link.h
+++ b/include/openthread/link.h
@@ -55,27 +55,6 @@ extern "C" {
#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 3c2938c45..39e14d431 100644
--- a/include/openthread/message.h
+++ b/include/openthread/message.h
@@ -91,6 +91,27 @@ typedef struct otMessageSettings
} 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 @@ void otMessageSetDirectTransmission(otMessage *aMessage, bool aEnabled);
/**
* 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 b077001e4..4d814c119 100644
--- a/include/openthread/thread.h
+++ b/include/openthread/thread.h
@@ -709,7 +709,7 @@ void otThreadSetKeySequenceCounter(otInstance *aInstance, uint32_t aKeySequenceC
* @sa otThreadSetKeySwitchGuardTime
*
*/
-uint32_t otThreadGetKeySwitchGuardTime(otInstance *aInstance);
+uint16_t otThreadGetKeySwitchGuardTime(otInstance *aInstance);
/**
* Sets the thrKeySwitchGuardTime (in hours).
@@ -723,7 +723,7 @@ uint32_t otThreadGetKeySwitchGuardTime(otInstance *aInstance);
* @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 5ed2e4e4b..f8e227c5d 100755
--- a/script/cmake-build
+++ b/script/cmake-build
@@ -109,6 +109,7 @@ OT_POSIX_SIM_COMMON_OPTIONS=(
"-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 491d4acb9..a0921af53 100755
--- a/script/make-pretty
+++ b/script/make-pretty
@@ -95,6 +95,7 @@ OT_CLANG_TIDY_BUILD_OPTS=(
'-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 318a4d0b9..b74577af1 100644
--- a/src/cli/BUILD.gn
+++ b/src/cli/BUILD.gn
@@ -55,8 +55,6 @@ openthread_cli_sources = [
"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 @@ openthread_cli_sources = [
"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 f36439f26..9dfee4030 100644
--- a/src/cli/CMakeLists.txt
+++ b/src/cli/CMakeLists.txt
@@ -45,13 +45,13 @@ set(COMMON_SOURCES
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 262b9f567..c8dfe2744 100644
--- a/src/cli/README.md
+++ b/src/cli/README.md
@@ -86,6 +86,7 @@ Done
- [networkkey](#networkkey)
- [networkname](#networkname)
- [networktime](#networktime)
+- [nexthop](#nexthop)
- [panid](#panid)
- [parent](#parent)
- [parentpriority](#parentpriority)
@@ -357,12 +358,99 @@ Done
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 @@ Set the CSL timeout in seconds.
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 @@ Done
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 @@ Done
### 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 @@ Set the Thread Network Name.
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 189de8a6f..8376290d6 100644
--- a/src/cli/README_BR.md
+++ b/src/cli/README_BR.md
@@ -36,6 +36,7 @@ omrprefix
onlinkprefix
pd
prefixtable
+raoptions
rioprf
routeprf
routers
@@ -235,6 +236,28 @@ prefix:1200:abba:baba:0::/64, on-link:yes, ms-since-rx:29527, lifetime:1800, pre
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 54f4c1e73..1f32cce97 100644
--- a/src/cli/README_DATASET.md
+++ b/src/cli/README_DATASET.md
@@ -107,6 +107,58 @@ After the device successfully attaches to a Thread network, the device will retr
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 @@ Normally, an active Commissioner will set a new Pending Operational Dataset. For
- [pskc](#pskc)
- [securitypolicy](#securitypolicy)
- [tlvs](#tlvs)
+- [updater](#updater)
## Command Details
@@ -695,3 +748,74 @@ Done
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 ad33bb993..940288596 100644
--- a/src/cli/README_NETDATA.md
+++ b/src/cli/README_NETDATA.md
@@ -334,10 +334,12 @@ Done
### 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 @@ Commissioning:
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 4d1ab0278..33d1123bc 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 @@ static OT_DEFINE_ALIGNED_VAR(sInterpreterRaw, sizeof(Interpreter), uint64_t);
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 @@ void Interpreter::ProcessLine(char *aBuf)
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 @@ otError Interpreter::SetUserCommands(const otCliCommand *aCommands, uint8_t aLen
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 @@ otError Interpreter::ParseJoinerDiscerner(Arg &aArg, otJoinerDiscerner &aDiscern
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 @@ template <> otError Interpreter::Process<Cmd("ba")>(Arg aArgs[])
}
}
#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 @@ template <> otError Interpreter::Process<Cmd("ba")>(Arg aArgs[])
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 @@ template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
}
}
#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 @@ template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
* @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 @@ template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
* @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 @@ template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
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 @@ template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
* @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 @@ template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
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 @@ template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
{
error = ProcessGetSet(aArgs + 2, otChannelManagerGetDelay, otChannelManagerSetDelay);
}
+#endif
/**
* @cli channel manager interval
* @code
@@ -1454,7 +1599,9 @@ template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
* @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 @@ template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
* @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 @@ template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
* @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 @@ template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
* @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 @@ template <> otError Interpreter::Process<Cmd("csl")>(Arg aArgs[])
*/
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 @@ void Interpreter::Initialize(otInstance *aInstance, otCliOutputCallback aCallbac
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 142f02c33..75ea62975 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 @@ extern "C" void otCliOutputFormat(const char *aFmt, ...);
* 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 @@ class Interpreter : public OutputImplementer, public Output
friend void otCliOutputFormat(const char *aFmt, ...);
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
@@ -179,19 +177,6 @@ public:
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 @@ private:
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 @@ private:
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 @@ private:
#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 d37d3dfcc..0f86b2441 100644
--- a/src/cli/cli_bbr.cpp
+++ b/src/cli/cli_bbr.cpp
@@ -290,8 +290,7 @@ template <> otError Bbr::Process<Cmd("disable")>(Arg aArgs[])
*/
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 40ed979f6..120518ac9 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 @@ namespace Cli {
* Implements the BBR CLI interpreter.
*
*/
-class Bbr : private Output
+class Bbr : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor.
*
@@ -64,7 +62,7 @@ public:
*
*/
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 9c77c7d07..53f2b3274 100644
--- a/src/cli/cli_br.cpp
+++ b/src/cli/cli_br.cpp
@@ -455,7 +455,7 @@ template <> otError Br::Process<Cmd("pd")>(Arg aArgs[])
* #otBorderRoutingDhcp6PdSetEnabled
*
*/
- if (Interpreter::GetInterpreter().ProcessEnableDisable(aArgs, otBorderRoutingDhcp6PdSetEnabled) == OT_ERROR_NONE)
+ if (ProcessEnableDisable(aArgs, otBorderRoutingDhcp6PdSetEnabled) == OT_ERROR_NONE)
{
}
/**
@@ -538,6 +538,46 @@ void Br::OutputRouterInfo(const otBorderRoutingRouterEntry &aEntry)
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 @@ otError Br::Process(Arg aArgs[])
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 01fcdcf94..5a877a84d 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 @@ namespace Cli {
* 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 @@ public:
*
*/
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 9464dff07..5edbe00c6 100644
--- a/src/cli/cli_coap.cpp
+++ b/src/cli/cli_coap.cpp
@@ -45,7 +45,7 @@ namespace ot {
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 @@ void Coap::HandleRequest(otMessage *aMessage, const otMessageInfo *aMessageInfo)
}
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 2b4184a51..95ce7b94a 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 @@ namespace Cli {
* 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 c1bacdb62..2ff4f6819 100644
--- a/src/cli/cli_coap_secure.cpp
+++ b/src/cli/cli_coap_secure.cpp
@@ -47,7 +47,7 @@ namespace ot {
namespace Cli {
CoapSecure::CoapSecure(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
, mShutdownFlag(false)
, mUseCertificate(false)
, mPskLength(0)
@@ -103,7 +103,7 @@ void CoapSecure::PrintPayload(otMessage *aMessage)
* @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 @@ exit:
* @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 @@ exit:
* `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 @@ exit:
* 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 @@ template <> otError CoapSecure::Process<Cmd("stop")>(Arg aArgs[])
* 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 @@ template <> otError CoapSecure::Process<Cmd("isclosed")>(Arg aArgs[])
* 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 @@ template <> otError CoapSecure::Process<Cmd("isconnected")>(Arg aArgs[])
* @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 @@ exit:
* `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 @@ template <> otError CoapSecure::Process<Cmd("get")>(Arg aArgs[]) { return Proces
* 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 @@ template <> otError CoapSecure::Process<Cmd("post")>(Arg aArgs[]) { return Proce
* 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 @@ exit:
* 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 a09dfed02..92a7a95e5 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 @@ namespace Cli {
* 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 162c6da5b..a5feac4cc 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 @@ namespace Cli {
* Implements the Commissioner CLI interpreter.
*
*/
-class Commissioner : private Output
+class Commissioner : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
@@ -62,7 +60,7 @@ public:
*
*/
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 cfd47a18c..63e320381 100644
--- a/src/cli/cli_dataset.cpp
+++ b/src/cli/cli_dataset.cpp
@@ -1154,10 +1154,46 @@ template <> otError Dataset::Process<Cmd("updater")>(Arg aArgs[])
{
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 @@ template <> otError Dataset::Process<Cmd("updater")>(Arg aArgs[])
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 858786231..06055e34a 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 @@ namespace Cli {
* 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 e5dff9a13..5148bb36b 100644
--- a/src/cli/cli_dns.cpp
+++ b/src/cli/cli_dns.cpp
@@ -679,8 +679,7 @@ template <> otError Dns::Process<Cmd("server")>(Arg aArgs[])
* @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 b9f167939..bf31ed76e 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 @@ namespace Cli {
* Implements the DNS CLI interpreter.
*
*/
-class Dns : private Output
+class Dns : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor.
*
@@ -76,7 +74,7 @@ public:
*
*/
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 90f832fbf..6958b0429 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 @@ namespace Cli {
* 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 @@ public:
*
*/
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 e45dbf8db..297525c74 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 @@ namespace Cli {
* Implements the Joiner CLI interpreter.
*
*/
-class Joiner : private Output
+class Joiner : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
@@ -62,7 +60,7 @@ public:
*
*/
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 93cc8c575..a12515d1b 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 ot {
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 d62be780e..40b67c604 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 @@ namespace Cli {
*
*/
-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 1394d4fa0..0f03b1b5b 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 @@ namespace Cli {
* 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 @@ public:
*
*/
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 85c3e262a..7aa0dd5ed 100644
--- a/src/cli/cli_network_data.cpp
+++ b/src/cli/cli_network_data.cpp
@@ -44,7 +44,7 @@ namespace ot {
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 @@ otError NetworkData::GetNextPrefix(otNetworkDataIterator *aIterator, otBorderRou
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)
+ {
+ 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)
{
- OutputPrefix(config);
+ OutputFormat(" e");
}
+
+ OutputNewLine();
+
+exit:
+ return;
}
otError NetworkData::GetNextRoute(otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig, bool aLocal)
@@ -597,19 +658,6 @@ otError NetworkData::GetNextRoute(otNetworkDataIterator *aIterator, otExternalRo
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 @@ otError NetworkData::GetNextService(otNetworkDataIterator *aIterator, otServiceC
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 @@ exit:
* 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 @@ exit:
*/
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 @@ template <> otError NetworkData::Process<Cmd("show")>(Arg aArgs[])
}
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 15af65084..ba8f281c4 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 @@ namespace Cli {
* 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 @@ public:
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 @@ private:
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 7d8825908..1111e3d1f 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 ot {
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 e517506bd..6504412a4 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 @@ namespace Cli {
*
*/
-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 d90947bc1..a5301f390 100644
--- a/src/cli/cli_srp_client.cpp
+++ b/src/cli/cli_srp_client.cpp
@@ -58,7 +58,7 @@ exit:
}
SrpClient::SrpClient(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
, mCallbackEnabled(false)
{
otSrpClientSetCallback(GetInstancePtr(), SrpClient::HandleCallback, this);
@@ -453,7 +453,7 @@ exit:
*/
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("leaseinterval")>(Arg aArgs[])
*/
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 @@ template <> otError SrpClient::Process<Cmd("service")>(Arg aArgs[])
* * --> [@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 @@ template <> otError SrpClient::Process<Cmd("service")>(Arg aArgs[])
{
// `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 @@ exit:
*/
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 93e7fe35f..85564cd91 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 @@ namespace Cli {
* 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 ee61a339f..5e71a4a24 100644
--- a/src/cli/cli_srp_server.cpp
+++ b/src/cli/cli_srp_server.cpp
@@ -121,8 +121,7 @@ template <> otError SrpServer::Process<Cmd("addrmode")>(Arg aArgs[])
*/
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 344d3c2bb..b110eb896 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 @@ namespace Cli {
* 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 @@ public:
*
*/
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 7845be89b..1ef73a561 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 91b36cc4b..697cba183 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 @@ namespace Cli {
* Implements the Tcat CLI interpreter.
*
*/
-class Tcat : private Output
+class Tcat : private Utils
{
public:
- typedef Utils::CmdLineParser::Arg Arg;
-
/**
* Constructor
*
@@ -58,7 +56,7 @@ public:
*
*/
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 90ce1aaef..b891c2df6 100644
--- a/src/cli/cli_tcp.cpp
+++ b/src/cli/cli_tcp.cpp
@@ -61,7 +61,7 @@ const int TcpExample::sCipherSuites[] = {MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8,
#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 50e728025..e658edee0 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 @@ namespace Cli {
* 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 9f8afdf2a..299be12ef 100644
--- a/src/cli/cli_udp.cpp
+++ b/src/cli/cli_udp.cpp
@@ -44,7 +44,7 @@ namespace ot {
namespace Cli {
UdpExample::UdpExample(otInstance *aInstance, OutputImplementer &aOutputImplementer)
- : Output(aInstance, aOutputImplementer)
+ : Utils(aInstance, aOutputImplementer)
, mLinkSecurityEnabled(true)
{
ClearAllBytes(mSocket);
@@ -408,7 +408,7 @@ otError UdpExample::PrepareHexStringPayload(otMessage &aMessage, const char *aHe
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 0c15c8070..d1c70508d 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 @@ namespace Cli {
* 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
index c70eacdd2..f2f53593d 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 @@ OutputImplementer::OutputImplementer(otCliOutputCallback aCallback, void *aCallb
{
}
-void Output::OutputFormat(const char *aFormat, ...)
+void Utils::OutputFormat(const char *aFormat, ...)
{
va_list args;
@@ -69,7 +69,7 @@ void Output::OutputFormat(const char *aFormat, ...)
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 @@ void Output::OutputFormat(uint8_t aIndentSize, const char *aFormat, ...)
va_end(args);
}
-void Output::OutputLine(const char *aFormat, ...)
+void Utils::OutputLine(const char *aFormat, ...)
{
va_list args;
@@ -91,7 +91,7 @@ void Output::OutputLine(const char *aFormat, ...)
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 @@ void Output::OutputLine(uint8_t aIndentSize, const char *aFormat, ...)
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::OutputBytes(const uint8_t *aBytes, uint16_t aLength)
}
}
-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 @@ const char *Output::Uint64ToString(uint64_t aUint64, Uint64StringBuffer &aBuffer
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 @@ void Output::OutputIp6Address(const otIp6Address &aAddress)
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 @@ void Output::OutputIp6Prefix(const otIp6Prefix &aPrefix)
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 @@ void Output::OutputSockAddr(const otSockAddr &aSockAddr)
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 @@ void Output::OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength)
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 @@ const char *Output::PercentageToString(uint16_t aValue, PercentageStringBuffer &
#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 @@ exit:
}
#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 @@ void Output::LogInput(const Arg *aArgs)
}
#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 @@ void Output::OutputTableHeader(uint8_t aNumColumns, const char *const aTitles[],
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 @@ void Output::OutputTableSeparator(uint8_t aNumColumns, const uint8_t aWidths[])
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
index 3029108cf..ebf9cc14a 100644
--- a/src/cli/cli_output.hpp
+++ b/src/cli/cli_utils.hpp
@@ -70,7 +70,7 @@ constexpr static CommandId Cmd(const char *aString)
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 Output;
*/
class OutputImplementer
{
- friend class Output;
+ friend class Utils;
public:
/**
@@ -111,13 +111,13 @@ private:
};
/**
- * 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 @@ public:
* @param[in] aImplementer An `OutputImplementer`.
*
*/
- Output(otInstance *aInstance, OutputImplementer &aImplementer)
+ Utils(otInstance *aInstance, OutputImplementer &aImplementer)
: mInstance(aInstance)
, mImplementer(aImplementer)
{
@@ -543,6 +543,108 @@ public:
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 @@ private:
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 449f99612..47652d2c6 100644
--- a/src/cli/radio.cmake
+++ b/src/cli/radio.cmake
@@ -45,7 +45,7 @@ target_include_directories(openthread-cli-radio PUBLIC ${OT_PUBLIC_INCLUDES} PRI
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 a866cd0d4..715741d58 100644
--- a/src/core/BUILD.gn
+++ b/src/core/BUILD.gn
@@ -41,6 +41,8 @@ if (openthread_enable_core_config_args) {
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 35901162a..6a9f42f50 100644
--- a/src/core/api/border_agent_api.cpp
+++ b/src/core/api/border_agent_api.cpp
@@ -64,4 +64,35 @@ uint16_t otBorderAgentGetUdpPort(otInstance *aInstance)
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 b62ee7041..7c8002dea 100644
--- a/src/core/api/border_routing_api.cpp
+++ b/src/core/api/border_routing_api.cpp
@@ -75,6 +75,11 @@ void otBorderRoutingClearRouteInfoOptionPreference(otInstance *aInstance)
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 af17e0d8a..3aa5d36a7 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 @@ otError otChannelManagerSetDelay(otInstance *aInstance, uint16_t aDelay)
#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 41dedc199..fe55b90e1 100644
--- a/src/core/api/message_api.cpp
+++ b/src/core/api/message_api.cpp
@@ -90,6 +90,11 @@ void otMessageSetDirectTransmission(otMessage *aMessage, bool aEnabled)
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 6bf2ca39d..97e5508f0 100644
--- a/src/core/api/thread_api.cpp
+++ b/src/core/api/thread_api.cpp
@@ -275,15 +275,15 @@ uint32_t otThreadGetKeySequenceCounter(otInstance *aInstance)
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 551cfa22d..4fb15e4bc 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 @@ void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage,
}
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 81f2d9abb..bebd795c8 100644
--- a/src/core/border_router/routing_manager.cpp
+++ b/src/core/border_router/routing_manager.cpp
@@ -363,6 +363,22 @@ exit:
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 @@ void RoutingManager::EvaluateRoutingPolicy(void)
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 @@ exit:
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 @@ void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode)
// 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));
- }
+ SuccessOrExit(error = raMsg.AppendBytes(mExtraRaOptions.GetBytes(), mExtraRaOptions.GetLength()));
}
-}
-bool RoutingManager::IsReceivedRouterAdvertFromManager(const Ip6::Nd::RouterAdvertMessage &aRaMessage) const
-{
- // Determines whether or not a received RA message was prepared by
- // by `RoutingManager` itself.
+ VerifyOrExit(raMsg.ContainsAnyOptions());
- bool isFromManager = false;
- uint16_t rioCount = 0;
- Ip6::Prefix prefix;
+ destAddress.SetToLinkLocalAllNodesMulticast();
+ raMsg.GetAsPacket(packet);
- VerifyOrExit(aRaMessage.ContainsAnyOptions());
+ mRaInfo.IncrementTxCountAndSaveHash(packet);
- 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);
+ SuccessOrExit(error = mInfraIf.Send(packet, destAddress));
- if (rio.GetRouteLifetime() != 0)
- {
- VerifyOrExit(mRioAdvertiser.HasAdvertised(prefix));
- rioCount++;
- }
-
- break;
- }
-
- default:
- ExitNow();
- }
- }
-
- VerifyOrExit(rioCount == mRioAdvertiser.GetAdvertisedRioCount());
-
- isFromManager = true;
+ 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 @@ bool RoutingManager::IsValidOmrPrefix(const Ip6::Prefix &aPrefix)
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::HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, co
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 @@ exit:
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 @@ exit:
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 @@ exit:
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 @@ bool RoutingManager::NetworkDataContainsUlaRoute(void) const
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 @@ RoutingManager::DiscoveredPrefixTable::DiscoveredPrefixTable(Instance &aInstance
{
}
-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 @@ void RoutingManager::DiscoveredPrefixTable::ProcessRouterAdvertMessage(const Ip6
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 @@ exit:
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 @@ exit:
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 @@ exit:
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 @@ exit:
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::RemoveExpiredEntries(void)
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::HandleRouterTimer(void)
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 @@ exit:
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 @@ void RoutingManager::DiscoveredPrefixTable::DetermineAndSetFlags(Ip6::Nd::Router
if (router.mManagedAddressConfigFlag)
{
- aRaMessage.GetHeader().SetManagedAddressConfigFlag();
+ aHeader.SetManagedAddressConfigFlag();
}
if (router.mOtherConfigFlag)
{
- aRaMessage.GetHeader().SetOtherConfigFlag();
+ aHeader.SetOtherConfigFlag();
}
}
}
@@ -1775,7 +1712,7 @@ void RoutingManager::DiscoveredPrefixTable::Iterator::Advance(AdvanceMode aMode)
//---------------------------------------------------------------------------------------------------------------------
// 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 @@ void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::Router
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 @@ void RoutingManager::DiscoveredPrefixTable::Entry::SetFrom(const Ip6::Nd::Prefix
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 @@ bool RoutingManager::OnLinkPrefixManager::IsPublishingOrAdvertising(void) const
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 @@ void RoutingManager::OnLinkPrefixManager::AppendCurPrefix(Ip6::Nd::RouterAdvertM
// 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 @@ void RoutingManager::OnLinkPrefixManager::AppendCurPrefix(Ip6::Nd::RouterAdvertM
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 @@ void RoutingManager::OnLinkPrefixManager::AppendOldPrefixes(Ip6::Nd::RouterAdver
}
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 @@ exit:
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 @@ void RoutingManager::RioAdvertiser::InvalidatPrevRios(Ip6::Nd::RouterAdvertMessa
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 @@ void RoutingManager::RioAdvertiser::AppendRios(Ip6::Nd::RouterAdvertMessage &aRa
{
if (now >= prefix.mExpirationTime)
{
- AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage);
+ SuccessOrExit(error = AppendRio(prefix.mPrefix, /* aRouteLifetime */ 0, aRaMessage));
continue;
}
}
@@ -2938,7 +2891,7 @@ void RoutingManager::RioAdvertiser::AppendRios(Ip6::Nd::RouterAdvertMessage &aRa
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 @@ void RoutingManager::RioAdvertiser::AppendRios(Ip6::Nd::RouterAdvertMessage &aRa
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 @@ exit:
#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 @@ void RoutingManager::RsSender::Stop(void) { mTimer.Stop(); }
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 @@ exit:
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 @@ exit:
}
}
-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 @@ Error RoutingManager::PdPrefixManager::Process(const Ip6::Nd::RouterAdvertMessag
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 4aef01d91..37de6eca3 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 @@ public:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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:
}
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 @@ private:
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 @@ private:
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 696d0fc83..6afd693a7 100644
--- a/src/core/coap/coap.cpp
+++ b/src/core/coap/coap.cpp
@@ -619,7 +619,6 @@ Error CoapBase::PrepareNextBlockRequest(Message::BlockType aType,
{
Error error = kErrorNone;
bool isOptionSet = false;
- uint64_t optionBuf = 0;
uint16_t blockOption = 0;
Option::Iterator iterator;
@@ -655,8 +654,9 @@ Error CoapBase::PrepareNextBlockRequest(Message::BlockType aType,
}
// 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 cbf544813..4998de7a6 100644
--- a/src/core/coap/coap_message.cpp
+++ b/src/core/coap/coap_message.cpp
@@ -146,8 +146,12 @@ uint8_t Message::WriteExtendedOptionField(uint16_t aValue, uint8_t *&aBuffer)
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 @@ Error Message::AppendOption(uint16_t aNumber, uint16_t aLength, const void *aVal
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 789c91939..62128b6a3 100644
--- a/src/core/coap/coap_message.hpp
+++ b/src/core/coap/coap_message.hpp
@@ -402,6 +402,23 @@ public:
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 @@ private:
}
uint8_t WriteExtendedOptionField(uint16_t aValue, uint8_t *&aBuffer);
+
+ Error AppendOptionHeader(uint16_t aNumber, uint16_t aLength);
};
/**
@@ -1187,6 +1206,16 @@ public:
*/
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 1809cd54b..afddcfda0 100644
--- a/src/core/common/log.cpp
+++ b/src/core/common/log.cpp
@@ -136,6 +136,16 @@ exit:
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 47293e0b3..49f0416e6 100644
--- a/src/core/common/log.hpp
+++ b/src/core/common/log.hpp
@@ -162,6 +162,22 @@ constexpr uint8_t kMaxLogModuleNameLength = 14; ///< Maximum module name length
#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 @@ public:
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 50e1fcc45..3a4c342e7 100644
--- a/src/core/common/message.cpp
+++ b/src/core/common/message.cpp
@@ -770,12 +770,19 @@ Message *Message::Clone(uint16_t aLength) const
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 @@ void Message::SetChildMask(uint16_t aChildIndex) { GetMetadata().mChildMask.Set(
bool Message::IsChildPending(void) const { return GetMetadata().mChildMask.HasAny(); }
#endif
-void Message::SetLinkInfo(const ThreadLinkInfo &aLinkInfo)
+Error Message::GetLinkInfo(ThreadLinkInfo &aLinkInfo) const
+{
+ 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)
{
- SetLinkSecurityEnabled(aLinkInfo.mLinkSecurity);
SetPanId(aLinkInfo.mPanId);
+ SetChannel(aLinkInfo.mChannel);
AddRss(aLinkInfo.mRss);
-#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
AddLqi(aLinkInfo.mLqi);
-#endif
+ 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 d48f719c2..f3dfd5402 100644
--- a/src/core/common/message.hpp
+++ b/src/core/common/message.hpp
@@ -200,9 +200,7 @@ protected:
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 @@ protected:
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 @@ public:
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 @@ public:
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 @@ public:
*/
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 @@ public:
*
*/
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 @@ public:
* @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 @@ private:
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 4019628d5..c759110bc 100644
--- a/src/core/common/string.cpp
+++ b/src/core/common/string.cpp
@@ -89,13 +89,16 @@ exit:
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 0c73bb603..358d7f842 100644
--- a/src/core/common/string.hpp
+++ b/src/core/common/string.hpp
@@ -85,8 +85,8 @@ static constexpr char kNullChar = '\0'; ///< null character.
* @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 48a3f07af..5158bdd85 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 9be5db107..ab3925eda 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 7c481c25d..4ecb3dc5b 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 b04e32ea6..70e0c12e0 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 0810c010e..d09a3cc80 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 4735ce325..307611089 100644
--- a/src/core/instance/instance.cpp
+++ b/src/core/instance/instance.cpp
@@ -230,7 +230,9 @@ Instance::Instance(void)
#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 1ebc01c8d..30b443b34 100644
--- a/src/core/instance/instance.hpp
+++ b/src/core/instance/instance.hpp
@@ -645,7 +645,9 @@ private:
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::PingSender &Instance::Get(void) { return mPingSender;
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 243db1d8a..e70fc982b 100644
--- a/src/core/mac/mac.cpp
+++ b/src/core/mac/mac.cpp
@@ -1134,9 +1134,13 @@ void Mac::RecordCcaStatus(bool aCcaSuccess, uint8_t aChannel)
}
// 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 @@ Error Mac::ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Neig
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 3e84465be..9946bb2c7 100644
--- a/src/core/mac/mac_frame.cpp
+++ b/src/core/mac/mac_frame.cpp
@@ -1220,9 +1220,11 @@ void Frame::SetEnhAckProbingIe(const uint8_t *aValue, uint8_t aLen)
{
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 142c6060b..24e7331d8 100644
--- a/src/core/meshcop/border_agent.cpp
+++ b/src/core/meshcop/border_agent.cpp
@@ -128,7 +128,7 @@ void BorderAgent::SendErrorMessage(const ForwardContext &aForwardContext, Error
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 @@ void BorderAgent::SendErrorMessage(const Coap::Message &aRequest, bool aSeparate
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 @@ BorderAgent::BorderAgent(Instance &aInstance)
#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 @@ template <> void BorderAgent::HandleTmf<kUriProxyTx>(Coap::Message &aMessage, co
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 @@ exit:
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 @@ Error BorderAgent::ForwardToCommissioner(Coap::Message &aForwardMessage, const M
LogInfo("Sent to commissioner");
exit:
- LogError("send to commissioner", error);
+ LogWarnOnError(error, "send to commissioner");
return error;
}
@@ -514,7 +520,7 @@ template <> void BorderAgent::HandleTmf<kUriRelayTx>(Coap::Message &aMessage, co
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 @@ Error BorderAgent::ForwardToLeader(const Coap::Message &aMessage, const Ip6::Mes
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 @@ void BorderAgent::HandleConnected(bool aConnected)
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 @@ void BorderAgent::Start(void)
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 @@ void BorderAgent::Stop(void)
{
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)
+{
+ Error error = kErrorNone;
+ uint16_t length = StringLength(aKeyString, kMaxEphemeralKeyLength + 1);
+
+ VerifyOrExit(mState == kStateStarted, error = kErrorInvalidState);
+ VerifyOrExit((length >= kMinEphemeralKeyLength) && (length <= kMaxEphemeralKeyLength), error = kErrorInvalidArgs);
+
+ if (!mUsingEphemeralKey)
+ {
+ 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;
+}
+
+void BorderAgent::ClearEphemeralKey(void)
{
- if (aError != kErrorNone)
+ VerifyOrExit(mUsingEphemeralKey);
+
+ LogInfo("Clearing ephemeral key");
+ mEphemeralKeyTimer.Stop();
+
+ switch (mState)
{
- LogWarn("Failed to %s: %s", aActionText, ErrorToString(aError));
+ 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;
}
-#endif
+
+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 9f721d662..933a95c76 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 @@ class BorderAgent : public InstanceLocator, private NonCopyable
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 @@ public:
* Starts the Border Agent service.
*
*/
- void Start(void);
+ void Start(void) { IgnoreError(Start(kUdpPort)); }
/**
* Stops the Border Agent service.
@@ -141,6 +167,75 @@ public:
*/
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 @@ public:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 92a52c12e..999d73224 100644
--- a/src/core/meshcop/commissioner.cpp
+++ b/src/core/meshcop/commissioner.cpp
@@ -302,9 +302,9 @@ exit:
if ((error != kErrorNone) && (error != kErrorAlready))
{
Get<Tmf::SecureAgent>().Stop();
+ LogWarnOnError(error, "start commissioner");
}
- LogError("start commissioner", error);
return error;
}
@@ -343,7 +343,11 @@ Error Commissioner::Stop(ResignMode aResignMode)
#endif
exit:
- LogError("stop commissioner", error);
+ if (error != kErrorAlready)
+ {
+ LogWarnOnError(error, "stop commissioner");
+ }
+
return error;
}
@@ -405,7 +409,8 @@ void Commissioner::SendCommissionerSet(void)
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 @@ void Commissioner::SendKeepAlive(uint16_t aSessionId)
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 15c547ba9..028595674 100644
--- a/src/core/meshcop/dataset.cpp
+++ b/src/core/meshcop/dataset.cpp
@@ -251,10 +251,10 @@ void Dataset::ConvertTo(Info &aDatasetInfo) const
}
}
-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 @@ void Dataset::Set(Type aType, const Dataset &aDataset)
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 131a271a2..5fbc7998e 100644
--- a/src/core/meshcop/dataset.hpp
+++ b/src/core/meshcop/dataset.hpp
@@ -76,6 +76,12 @@ public:
};
/**
+ * 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 @@ public:
/**
* 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 @@ public:
/**
* 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 edb810871..ba4c5fc5a 100644
--- a/src/core/meshcop/dataset_local.cpp
+++ b/src/core/meshcop/dataset_local.cpp
@@ -147,15 +147,15 @@ exit:
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 @@ exit:
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 249f069d5..40e52bd61 100644
--- a/src/core/meshcop/dataset_local.hpp
+++ b/src/core/meshcop/dataset_local.hpp
@@ -134,13 +134,13 @@ public:
/**
* 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 @@ public:
/**
* 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 8bf1cb38a..6c49a239a 100644
--- a/src/core/meshcop/dataset_manager.cpp
+++ b/src/core/meshcop/dataset_manager.cpp
@@ -160,11 +160,11 @@ exit:
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 @@ exit:
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 @@ exit:
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 36162a315..c0d90fe5f 100644
--- a/src/core/meshcop/dataset_manager.hpp
+++ b/src/core/meshcop/dataset_manager.hpp
@@ -96,13 +96,13 @@ public:
/**
* 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 @@ protected:
/**
* 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 @@ public:
/**
* 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 @@ public:
*
* 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 fb0f3e61c..4df5f96fc 100644
--- a/src/core/meshcop/joiner.cpp
+++ b/src/core/meshcop/joiner.cpp
@@ -185,7 +185,7 @@ exit:
FreeJoinerFinalizeMessage();
}
- LogError("start joiner", error);
+ LogWarnOnError(error, "start joiner");
return error;
}
@@ -378,7 +378,7 @@ Error Joiner::Connect(JoinerRouter &aRouter)
SetState(kStateConnect);
exit:
- LogError("start secure joiner connection", error);
+ LogWarnOnError(error, "start secure joiner connection");
return error;
}
@@ -543,7 +543,7 @@ template <> void Joiner::HandleTmf<kUriJoinerEntrust>(Coap::Message &aMessage, c
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 acadf1a9d..a5df41367 100644
--- a/src/core/meshcop/joiner_router.cpp
+++ b/src/core/meshcop/joiner_router.cpp
@@ -232,7 +232,7 @@ void JoinerRouter::DelaySendingJoinerEntrust(const Ip6::MessageInfo &aMessageInf
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 b30fe274c..8bce74582 100644
--- a/src/core/meshcop/meshcop.cpp
+++ b/src/core/meshcop/meshcop.cpp
@@ -343,15 +343,5 @@ exit:
}
#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 339872bb3..3620927a0 100644
--- a/src/core/meshcop/meshcop.hpp
+++ b/src/core/meshcop/meshcop.hpp
@@ -561,22 +561,6 @@ Error GeneratePskc(const char *aPassPhrase,
*/
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 64c028d1b..4fc83854d 100644
--- a/src/core/meshcop/meshcop_leader.cpp
+++ b/src/core/meshcop/meshcop_leader.cpp
@@ -125,7 +125,7 @@ void Leader::SendPetitionResponse(const Coap::Message &aRequest,
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 @@ void Leader::SendKeepAliveResponse(const Coap::Message &aRequest,
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 @@ void Leader::SendDatasetChanged(const Ip6::Address &aAddress)
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 f8df7737e..f8e459642 100644
--- a/src/core/meshcop/tcat_agent.cpp
+++ b/src/core/meshcop/tcat_agent.cpp
@@ -94,7 +94,7 @@ Error TcatAgent::Start(const TcatAgent::VendorInfo &aVendorInfo,
mAlreadyCommissioned = false;
exit:
- LogError("start TCAT agent", error);
+ LogWarnOnError(error, "start TCAT agent");
return error;
}
@@ -461,9 +461,9 @@ exit:
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 @@ exit:
return error;
}
-#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
-void TcatAgent::LogError(const char *aActionText, Error aError)
-{
- if (aError != kErrorNone)
- {
- LogWarn("Failed to %s: %s", aActionText, ErrorToString(aError));
- }
-}
-#endif
-
} // namespace MeshCoP
} // namespace ot
diff --git a/src/core/meshcop/tcat_agent.hpp b/src/core/meshcop/tcat_agent.hpp
index d2dd9f340..16cf8f977 100644
--- a/src/core/meshcop/tcat_agent.hpp
+++ b/src/core/meshcop/tcat_agent.hpp
@@ -325,12 +325,6 @@ private:
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 e206ff664..56654fb47 100644
--- a/src/core/net/dhcp6_client.cpp
+++ b/src/core/net/dhcp6_client.cpp
@@ -288,7 +288,7 @@ exit:
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 7fd072cf4..ea9570737 100644
--- a/src/core/net/dhcp6_server.cpp
+++ b/src/core/net/dhcp6_server.cpp
@@ -172,11 +172,8 @@ void Server::AddPrefixAgent(const Ip6::Prefix &aIp6Prefix, const Lowpan::Context
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 385eb88f3..e05a7d9c1 100644
--- a/src/core/net/dnssd_server.cpp
+++ b/src/core/net/dnssd_server.cpp
@@ -170,7 +170,7 @@ void Server::ProcessQuery(Request &aRequest)
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 0c0bf5abd..d5b2e27ed 100644
--- a/src/core/net/ip6.cpp
+++ b/src/core/net/ip6.cpp
@@ -616,7 +616,7 @@ exit:
return error;
}
-Error Ip6::HandleFragment(Message &aMessage, MessageInfo &aMessageInfo)
+Error Ip6::HandleFragment(Message &aMessage)
{
Error error = kErrorNone;
Header header, headerBuffer;
@@ -703,8 +703,7 @@ Error Ip6::HandleFragment(Message &aMessage, MessageInfo &aMessageInfo)
mReassemblyList.Dequeue(*message);
- IgnoreError(HandleDatagram(OwnedPtr<Message>(message), aMessageInfo.mLinkInfo,
- /* aIsReassembled */ true));
+ IgnoreError(HandleDatagram(OwnedPtr<Message>(message), /* aIsReassembled */ true));
}
exit:
@@ -715,7 +714,7 @@ exit:
mReassemblyList.DequeueAndFree(*message);
}
- LogWarn("Reassembly failed: %s", ErrorToString(error));
+ LogWarnOnError(error, "reassemble");
}
if (isFragmented)
@@ -766,16 +765,12 @@ void Ip6::SendIcmpError(Message &aMessage, Icmp::Header::Type aIcmpType, Icmp::H
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 @@ Error Ip6::FragmentDatagram(Message &aMessage, uint8_t aIpProto)
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 @@ Error Ip6::HandleExtensionHeaders(OwnedPtr<Message> &aMessagePtr,
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 @@ Error Ip6::HandlePayload(Header &aIp6Header,
}
exit:
- if (error != kErrorNone)
- {
- LogNote("Failed to handle payload: %s", ErrorToString(error));
- }
-
+ LogWarnOnError(error, "handle payload");
return error;
}
@@ -959,17 +948,6 @@ Error Ip6::PassToHost(OwnedPtr<Message> &aMessagePtr,
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 @@ exit:
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 @@ Error Ip6::HandleDatagram(OwnedPtr<Message> aMessagePtr, const void *aLinkMessag
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 @@ Error Ip6::HandleDatagram(OwnedPtr<Message> aMessagePtr, const void *aLinkMessag
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 53330b359..de04eef8d 100644
--- a/src/core/net/ip6.hpp
+++ b/src/core/net/ip6.hpp
@@ -207,7 +207,6 @@ public:
* 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 @@ public:
* @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 @@ private:
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 e66fb043f..d73aea103 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 @@ void RaFlagsExtOption::Init(void)
}
//----------------------------------------------------------------------------------------------------------------------
-// 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 @@ void RouterAdvertMessage::Header::SetToDefault(void)
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 @@ Option *RouterAdvertMessage::AppendOption(uint16_t aOptionSize)
// 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 @@ exit:
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 @@ exit:
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 f4a6d5a25..4178f181f 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 @@ typedef NetworkData::RoutePreference RoutePreference; ///< Route Preference
OT_TOOL_PACKED_BEGIN
class Option
{
- friend class RouterAdvertMessage;
+ friend class RouterAdvert;
public:
enum Type : uint8_t
@@ -525,14 +526,14 @@ private:
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 @@ public:
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)
+ {
+ }
- /**
- * 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.
- *
- */
- template <uint16_t kBufferSize>
- RouterAdvertMessage(const Header &aHeader, uint8_t (&aBuffer)[kBufferSize])
- : mMaxLength(kBufferSize)
- {
- static_assert(kBufferSize >= sizeof(Header), "Buffer for RA msg is too small");
+ /**
+ * Gets the RA message as an `Icmp6Packet`.
+ *
+ * @returns The RA message as an `Icmp6Packet`.
+ *
+ */
+ const Icmp6Packet &GetAsPacket(void) const { return mData; }
- memcpy(aBuffer, &aHeader, sizeof(Header));
- mData.Init(aBuffer, sizeof(Header));
- }
+ /**
+ * 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 as an `Icmp6Packet`.
- *
- * @returns The RA message as an `Icmp6Packet`.
- *
- */
- const Icmp6Packet &GetAsPacket(void) const { return mData; }
+ /**
+ * 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;
+ };
/**
- * 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.
+ * Represents an RA message to be sent.
*
*/
- bool IsValid(void) const
+ class TxMessage
{
- return (mData.GetBytes() != nullptr) && (mData.GetLength() >= sizeof(Header)) &&
- (GetHeader().GetType() == Icmp::Header::kTypeRouterAdvert);
- }
+ 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()); }
- /**
- * 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 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's header.
- *
- * @returns The RA message's header.
- *
- */
- Header &GetHeader(void) { return *reinterpret_cast<Header *>(AsNonConst(mData.GetBytes())); }
+ /**
+ * 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);
- /**
- * 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);
+ /**
+ * 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);
- /**
- * 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);
+ /**
+ * 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);
- /**
- * 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);
+ /**
+ * 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);
- /**
- * 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)); }
+ /**
+ * 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)); }
- // The following methods are intended to support range-based `for`
- // loop iteration over `Option`s in the RA message.
+ private:
+ static constexpr uint16_t kCapacityIncrement = 256;
- Option::Iterator begin(void) const { return Option::Iterator(GetOptionStart(), GetDataEnd()); }
- Option::Iterator end(void) const { return Option::Iterator(); }
+ Option *AppendOption(uint16_t aOptionSize);
-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);
+ Heap::Array<uint8_t, kCapacityIncrement> mArray;
+ };
- 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 5897a5f3b..7718b02ba 100644
--- a/src/core/net/sntp_client.cpp
+++ b/src/core/net/sntp_client.cpp
@@ -193,7 +193,7 @@ exit:
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 fb4e928ff..e1ca9ac7b 100644
--- a/src/core/net/socket.hpp
+++ b/src/core/net/socket.hpp
@@ -181,30 +181,6 @@ public:
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 3d6550dae..635cbedb2 100644
--- a/src/core/net/srp_server.cpp
+++ b/src/core/net/srp_server.cpp
@@ -91,7 +91,7 @@ Server::Server(Instance &aInstance)
, mOutstandingUpdatesTimer(aInstance)
, mCompletedUpdateTask(aInstance)
, mServiceUpdateId(Random::NonCrypto::GetUint32())
- , mPort(kUdpPortMin)
+ , mPort(kUninitializedPort)
, mState(kStateDisabled)
, mAddressMode(kDefaultAddressMode)
, mAnycastSequenceNumber(0)
@@ -595,7 +595,7 @@ exit:
}
}
-void Server::SelectPort(void)
+void Server::InitPort(void)
{
mPort = kUdpPortMin;
@@ -605,24 +605,35 @@ void Server::SelectPort(void)
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 @@ void Server::Start(void)
#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 @@ void Server::PrepareSocket(void)
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 @@ void Server::HandleDnssdServerStateChange(void)
if (mState == kStateRunning)
{
- PrepareSocket();
+ IgnoreError(PrepareSocket());
}
}
@@ -811,6 +830,13 @@ void Server::ProcessDnsUpdate(Message &aMessage, MessageMetadata &aMetadata)
// 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 @@ Error Server::ProcessZoneSection(const Message &aMessage, MessageMetadata &aMeta
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 @@ Error Server::ProcessUpdateSection(Host &aHost, const Message &aMessage, Message
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 @@ Error Server::ProcessHostDescriptionInstruction(Host &aHost,
// 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 @@ Error Server::ProcessServiceDiscoveryInstructions(Host &aHost,
}
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 @@ Error Server::ProcessServiceDescriptionInstructions(Host &aHost,
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 @@ Error Server::ProcessAdditionalSection(Host *aHost, const Message &aMessage, Mes
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 Server::VerifySignature(const Host::Key &aKey,
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 @@ void Server::SendResponse(const Dns::UpdateHeader &aHeader,
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 @@ void Server::SendResponse(const Dns::UpdateHeader &aHeader,
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 @@ void Server::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessag
{
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 @@ void Server::UpdateResponseCounters(Dns::UpdateHeader::Response aResponseCode)
}
}
+#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 6b5556d4e..ebe67d8fe 100644
--- a/src/core/net/srp_server.hpp
+++ b/src/core/net/srp_server.hpp
@@ -912,6 +912,7 @@ private:
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 @@ private:
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 @@ private:
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 498eb627c..009b671f9 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 5e5224468..8f4a0a94c 100644
--- a/src/core/radio/ble_secure.cpp
+++ b/src/core/radio/ble_secure.cpp
@@ -89,7 +89,14 @@ exit:
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 @@ exit:
Error BleSecure::Connect(void)
{
Ip6::SockAddr sockaddr;
+ Error error;
+
+ VerifyOrExit(mBleState == kConnected, error = kErrorInvalidState);
- return mTls.Connect(sockaddr);
+ error = mTls.Connect(sockaddr);
+
+exit:
+ return error;
}
void BleSecure::Disconnect(void)
@@ -137,8 +150,11 @@ void BleSecure::Disconnect(void)
if (mBleState == kConnected)
{
+ mBleState = kAdvertising;
IgnoreReturnValue(otPlatBleGapDisconnect(&GetInstance()));
}
+
+ mConnectCallback.InvokeIfSet(&GetInstance(), false, false);
}
void BleSecure::SetPsk(const MeshCoP::JoinerPskd &aPskd)
@@ -278,12 +294,7 @@ void BleSecure::HandleBleDisconnected(uint16_t aConnectionId)
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 18051615d..c8bb938a3 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 @@ inline Error Radio::ResetCsl(void) { return kErrorNotImplemented; }
#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 6dcb85de5..0f6001246 100644
--- a/src/core/radio/radio_platform.cpp
+++ b/src/core/radio/radio_platform.cpp
@@ -261,14 +261,14 @@ OT_TOOL_WEAK uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)
{
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 880d436ae..8b5d0a422 100644
--- a/src/core/thread/address_resolver.hpp
+++ b/src/core/thread/address_resolver.hpp
@@ -199,7 +199,10 @@ public:
/**
* 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 77eebcdaf..7b3d12940 100644
--- a/src/core/thread/csl_tx_scheduler.hpp
+++ b/src/core/thread/csl_tx_scheduler.hpp
@@ -113,7 +113,7 @@ public:
* 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 3a59651d8..9cd997892 100644
--- a/src/core/thread/discover_scanner.cpp
+++ b/src/core/thread/discover_scanner.cpp
@@ -308,8 +308,7 @@ exit:
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 @@ void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const
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 7d78879a3..9f98b0744 100644
--- a/src/core/thread/dua_manager.cpp
+++ b/src/core/thread/dua_manager.cpp
@@ -158,7 +158,7 @@ Error DuaManager::GenerateDomainUnicastAddressIid(void)
}
else
{
- LogWarn("Generate DUA: %s", ErrorToString(error));
+ LogWarnOnError(error, "generate DUA");
}
return error;
@@ -548,7 +548,7 @@ exit:
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 258580580..c79c7d849 100644
--- a/src/core/thread/energy_scan_server.cpp
+++ b/src/core/thread/energy_scan_server.cpp
@@ -198,7 +198,7 @@ void EnergyScanServer::SendReport(void)
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 3d9337d93..d38fdb8b5 100644
--- a/src/core/thread/key_manager.cpp
+++ b/src/core/thread/key_manager.cpp
@@ -60,6 +60,9 @@ const uint8_t KeyManager::kTrelInfoString[] = {'T', 'h', 'r', 'e', 'a', 'd', 'O'
'r', 'I', 'n', 'f', 'r', 'a', 'K', 'e', 'y'};
#endif
+//---------------------------------------------------------------------------------------------------------------------
+// SecurityPolicy
+
void SecurityPolicy::SetToDefault(void)
{
mRotationTime = kDefaultKeyRotationTime;
@@ -163,6 +166,9 @@ exit:
return;
}
+//---------------------------------------------------------------------------------------------------------------------
+// KeyManager
+
KeyManager::KeyManager(Instance &aInstance)
: InstanceLocator(aInstance)
, mKeySequence(0)
@@ -171,7 +177,7 @@ KeyManager::KeyManager(Instance &aInstance)
, mStoredMleFrameCounter(0)
, mHoursSinceKeyRotation(0)
, mKeySwitchGuardTime(kDefaultKeySwitchGuardTime)
- , mKeySwitchGuardEnabled(false)
+ , mKeySwitchGuardTimer(0)
, mKeyRotationTimer(aInstance)
, mKekFrameCounter(0)
, mIsPskcSet(false)
@@ -198,8 +204,8 @@ KeyManager::KeyManager(Instance &aInstance)
void KeyManager::Start(void)
{
- mKeySwitchGuardEnabled = false;
- StartKeyRotationTimer();
+ mKeySwitchGuardTimer = 0;
+ ResetKeyRotationTimer();
}
void KeyManager::Stop(void) { mKeyRotationTimer.Stop(); }
@@ -362,20 +368,13 @@ void KeyManager::UpdateKeyMaterial(void)
#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 @@ void KeyManager::SetCurrentKeySequence(uint32_t aKeySequence)
SetAllMacFrameCounters(0, /* aSetIfLarger */ false);
mMleFrameCounter = 0;
+ ResetKeyRotationTimer();
+ mKeySwitchGuardTimer = mKeySwitchGuardTime;
+
Get<Notifier>().Signal(kEventThreadKeySeqCounterChanged);
exit:
@@ -476,40 +478,57 @@ void KeyManager::SetKek(const Kek &aKek)
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 18f11f20b..099854c45 100644
--- a/src/core/thread/key_manager.hpp
+++ b/src/core/thread/key_manager.hpp
@@ -77,8 +77,17 @@ public:
*/
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 @@ class KeyManager : public InstanceLocator, private NonCopyable
{
public:
/**
+ * Determines whether to apply or ignore key switch guard when updating the key sequence.
+ *
+ * Used as input by `SetCurrentKeySequence()`.
+ *
+ */
+ 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 @@ public:
/**
* 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 @@ public:
* @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 @@ public:
#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 @@ private:
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 @@ private:
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 bf6b35b42..67475401b 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 @@ exit:
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 df1aa2504..4b5b4b20a 100644
--- a/src/core/thread/mesh_forwarder.cpp
+++ b/src/core/thread/mesh_forwarder.cpp
@@ -1477,7 +1477,7 @@ void MeshForwarder::HandleFragment(FrameData &aFrameData,
message->SetDatagramTag(fragmentHeader.GetDatagramTag());
message->SetTimestampToNow();
- message->SetLinkInfo(aLinkInfo);
+ message->UpdateLinkInfoFrom(aLinkInfo);
VerifyOrExit(Get<Ip6::Filter>().Accept(*message), error = kErrorDrop);
@@ -1530,9 +1530,7 @@ void MeshForwarder::HandleFragment(FrameData &aFrameData,
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 @@ exit:
if (message->GetOffset() >= message->GetLength())
{
mReassemblyList.Dequeue(*message);
- IgnoreError(HandleDatagram(*message, aLinkInfo, aMacAddrs.mSource));
+ IgnoreError(HandleDatagram(*message, aMacAddrs.mSource));
}
}
else
@@ -1643,7 +1641,7 @@ void MeshForwarder::HandleLowpanHC(const FrameData &aFrameData,
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 @@ exit:
if (error == kErrorNone)
{
- IgnoreError(HandleDatagram(*message, aLinkInfo, aMacAddrs.mSource));
+ IgnoreError(HandleDatagram(*message, aMacAddrs.mSource));
}
else
{
@@ -1664,7 +1662,7 @@ exit:
}
}
-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 @@ Error MeshForwarder::HandleDatagram(Message &aMessage, const ThreadLinkInfo &aLi
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 8c3a7907c..1ccd0d0ba 100644
--- a/src/core/thread/mesh_forwarder.hpp
+++ b/src/core/thread/mesh_forwarder.hpp
@@ -550,7 +550,7 @@ private:
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 e91ceb19d..d4911b9a8 100644
--- a/src/core/thread/mesh_forwarder_ftd.cpp
+++ b/src/core/thread/mesh_forwarder_ftd.cpp
@@ -627,10 +627,16 @@ Error MeshForwarder::CheckReachability(const FrameData &aFrameData, const Mac::A
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 @@ void MeshForwarder::HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSo
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 7eeb25dca..99e6809f8 100644
--- a/src/core/thread/mle.cpp
+++ b/src/core/thread/mle.cpp
@@ -378,7 +378,7 @@ void Mle::Restore(void)
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 @@ void Mle::Restore(void)
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 @@ Error Mle::BecomeDetached(void)
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 @@ void Mle::InitNeighbor(Neighbor &aNeighbor, const RxInfo &aRxInfo)
{
aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(aNeighbor.GetExtAddress());
aNeighbor.GetLinkInfo().Clear();
- aNeighbor.GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss());
+ aNeighbor.GetLinkInfo().AddRss(aRxInfo.mMessage.GetAverageRss());
aNeighbor.ResetLinkFailures();
aNeighbor.SetLastHeard(TimerMilli::GetNow());
}
@@ -1120,7 +1121,6 @@ void Mle::HandleNotifierEvents(Events aEvents)
{
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 @@ void Mle::HandleNotifierEvents(Events aEvents)
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 @@ uint32_t Mle::Reattach(void)
}
else if (!IsRxOnWhenIdle())
{
- // return to sleepy operation
+ // Return to sleepy operation
Get<DataPollSender>().SetAttachMode(false);
Get<MeshForwarder>().SetRxOnWhenIdle(false);
}
@@ -1653,7 +1656,6 @@ void Mle::SendDelayedResponse(TxMessage &aMessage, const DelayedResponseMetadata
exit:
if (error != kErrorNone)
{
- // do not use `FreeMessageOnError()` to avoid null check on nonnull pointer
aMessage.Free();
}
}
@@ -1782,9 +1784,13 @@ Error Mle::SendChildIdRequest(void)
{
// 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 @@ Error Mle::SendChildIdRequest(void)
{
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 @@ void Mle::HandleMessageTransmissionTimer(void)
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 @@ Error Mle::SendChildUpdateRequest(ChildUpdateRequestMode aMode)
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 @@ void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageIn
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 @@ void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageIn
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 @@ exit:
// 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 @@ void Mle::ProcessKeySequence(RxInfo &aRxInfo)
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 @@ void Mle::ProcessKeySequence(RxInfo &aRxInfo)
{
if (aRxInfo.mKeySequence - Get<KeyManager>().GetCurrentKeySequence() == 1)
{
- Get<KeyManager>().SetCurrentKeySequence(aRxInfo.mKeySequence);
+ Get<KeyManager>().SetCurrentKeySequence(aRxInfo.mKeySequence, KeyManager::kApplyKeySwitchGuard);
}
else
{
@@ -2875,11 +2885,8 @@ void Mle::HandleDataResponse(RxInfo &aRxInfo)
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 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo)
uint16_t pendingDatasetLength = 0;
bool dataRequest = false;
- // Leader Data
SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
if ((leaderData.GetPartitionId() != mLeaderData.GetPartitionId()) ||
@@ -2934,7 +2940,6 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo)
VerifyOrExit(IsNetworkDataNewer(leaderData));
}
- // Active Timestamp
switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, activeTimestamp))
{
case kErrorNone:
@@ -2942,8 +2947,9 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo)
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 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo)
ExitNow(error = kErrorParse);
}
- // Pending Timestamp
switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, pendingTimestamp))
{
case kErrorNone:
@@ -2968,8 +2973,6 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo)
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 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo)
else
#endif
{
- // Active Dataset
if (hasActiveTimestamp)
{
if (activeDatasetOffset > 0)
@@ -3017,7 +3019,6 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo)
}
}
- // Pending Dataset
if (hasPendingTimestamp)
{
if (pendingDatasetOffset > 0)
@@ -3132,7 +3133,7 @@ exit:
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 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo)
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 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo)
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 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo)
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 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo)
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 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo)
#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 @@ void Mle::HandleChildIdResponse(RxInfo &aRxInfo)
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 @@ void Mle::HandleChildIdResponse(RxInfo &aRxInfo)
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 @@ void Mle::HandleChildIdResponse(RxInfo &aRxInfo)
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 @@ void Mle::HandleChildIdResponse(RxInfo &aRxInfo)
}
#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 @@ void Mle::HandleChildUpdateRequest(RxInfo &aRxInfo)
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 @@ void Mle::HandleChildUpdateRequest(RxInfo &aRxInfo)
ExitNow();
}
- // Leader Data, Network Data, Active Timestamp, Pending Timestamp
SuccessOrExit(error = HandleLeaderData(aRxInfo));
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
@@ -3517,7 +3496,8 @@ void Mle::HandleChildUpdateRequest(RxInfo &aRxInfo)
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 @@ void Mle::HandleChildUpdateRequest(RxInfo &aRxInfo)
}
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 @@ void Mle::HandleChildUpdateRequest(RxInfo &aRxInfo)
}
#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 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo)
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 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo)
OT_FALL_THROUGH;
case kRoleChild:
- // Source Address
SuccessOrExit(error = Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress));
if (RouterIdFromRloc16(sourceAddress) != RouterIdFromRloc16(GetRloc16()))
@@ -3640,10 +3617,8 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo)
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 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo)
{
Mac::CslAccuracy cslAccuracy;
- // CSL Accuracy
switch (aRxInfo.mMessage.ReadCslClockAccuracyTlv(cslAccuracy))
{
case kErrorNone:
@@ -3815,8 +3789,23 @@ void Mle::HandleLinkMetricsManagementRequest(RxInfo &aRxInfo)
exit:
LogProcessError(kTypeLinkMetricsManagementRequest, error);
}
+#endif
+
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+void Mle::HandleTimeSync(RxInfo &aRxInfo)
+{
+ Log(kMessageReceive, kTypeTimeSync, aRxInfo.mMessageInfo.GetPeerAddr());
-#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
+ 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 @@ void Mle::HandleLinkMetricsManagementResponse(RxInfo &aRxInfo)
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 @@ void Mle::HandleLinkProbe(RxInfo &aRxInfo)
exit:
LogProcessError(kTypeLinkProbe, error);
}
-#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
+#endif
void Mle::ProcessAnnounce(void)
{
@@ -3916,22 +3905,6 @@ bool Mle::IsAnycastLocator(const Ip6::Address &aAddress) const
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 @@ void Mle::InformPreviousParent(void)
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 @@ const char *Mle::MessageTypeToString(MessageType aType)
"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 @@ const char *Mle::MessageTypeToString(MessageType aType)
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 33d0920cf..873e33835 100644
--- a/src/core/thread/mle.hpp
+++ b/src/core/thread/mle.hpp
@@ -973,15 +973,15 @@ private:
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 @@ private:
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 @@ private:
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 76b8dc370..b55341f9b 100644
--- a/src/core/thread/mle_router.cpp
+++ b/src/core/thread/mle_router.cpp
@@ -363,15 +363,22 @@ void MleRouter::HandleChildStart(AttachMode aMode)
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):
+ //
+ // "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."
//
- // 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.
+ // 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 @@ void MleRouter::SetStateRouterOrLeader(DeviceRole aRole, uint16_t aRloc16, Leade
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 @@ void MleRouter::SendAdvertisement(void)
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 @@ void MleRouter::HandleLinkRequest(RxInfo &aRxInfo)
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 @@ void MleRouter::HandleLinkRequest(RxInfo &aRxInfo)
ExitNow(error = kErrorParse);
}
- // Source Address
switch (Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress))
{
case kErrorNone:
@@ -711,7 +716,8 @@ void MleRouter::HandleLinkRequest(RxInfo &aRxInfo)
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 @@ void MleRouter::HandleLinkRequest(RxInfo &aRxInfo)
ExitNow(error = kErrorParse);
}
- // TLV Request
switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList))
{
case kErrorNone:
@@ -748,16 +753,16 @@ void MleRouter::HandleLinkRequest(RxInfo &aRxInfo)
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 @@ Error MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo,
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 @@ Error MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo,
}
#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 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
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 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
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 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
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 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
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 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
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 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
case kRoleLeader:
VerifyOrExit(router != nullptr);
- // Leader Data
SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData));
VerifyOrExit(leaderData.GetPartitionId() == mLeaderData.GetPartitionId());
@@ -992,7 +986,6 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr()));
}
- // Route (optional)
switch (aRxInfo.mMessage.ReadRouteTlv(routeTlv))
{
case kErrorNone:
@@ -1026,7 +1019,6 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
OT_ASSERT(false);
}
- // finish link synchronization
InitNeighbor(*router, aRxInfo);
router->SetRloc16(sourceAddress);
router->GetLinkFrameCounters().SetAll(linkFrameCounter);
@@ -1049,10 +1041,8 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
RxChallenge challenge;
TlvList requestedTlvList;
- // Challenge
SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(challenge));
- // TLV Request
switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList))
{
case kErrorNone:
@@ -1062,7 +1052,7 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest)
ExitNow(error = kErrorParse);
}
- SuccessOrExit(error = SendLinkAccept(aRxInfo.mMessageInfo, router, requestedTlvList, challenge));
+ SuccessOrExit(error = SendLinkAccept(aRxInfo, router, requestedTlvList, challenge));
}
exit:
@@ -1178,7 +1168,7 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo, uint16_t aSourceAddress, c
// - `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 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo, uint16_t aSourceAddress, c
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 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo, uint16_t aSourceAddress, c
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 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo, uint16_t aSourceAddress, c
exit:
if (aRxInfo.mNeighbor && aRxInfo.mNeighbor->GetRloc16() != aSourceAddress)
{
- // Remove stale neighbors
RemoveNeighbor(*aRxInfo.mNeighbor);
}
@@ -1395,11 +1386,9 @@ void MleRouter::HandleParentRequest(RxInfo &aRxInfo)
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 @@ void MleRouter::HandleParentRequest(RxInfo &aRxInfo)
break;
}
- // Challenge
SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(challenge));
child = mChildTable.FindChild(extAddr, Child::kInStateAnyExceptInvalid);
@@ -1428,7 +1416,6 @@ void MleRouter::HandleParentRequest(RxInfo &aRxInfo)
{
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 @@ void MleRouter::HandleTimeTick(void)
mPreviousPartitionIdTimeout--;
}
+ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // Role transitions
+
roleTransitionTimeoutExpired = mRouterRoleTransition.HandleTimeTick();
switch (mRole)
@@ -1532,7 +1522,6 @@ void MleRouter::HandleTimeTick(void)
}
else
{
- // send announce after decided to stay in REED if needed
InformPreviousChannel();
}
@@ -1549,11 +1538,11 @@ void MleRouter::HandleTimeTick(void)
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 @@ void MleRouter::HandleTimeTick(void)
OT_ASSERT(false);
}
- // update children state
+ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // Update `ChildTable`
+
for (Child &child : Get<ChildTable>().Iterate(Child::kInStateAnyExceptInvalid))
{
uint32_t timeout = 0;
@@ -1605,7 +1596,7 @@ void MleRouter::HandleTimeTick(void)
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 @@ void MleRouter::HandleTimeTick(void)
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 @@ void MleRouter::HandleTimeTick(void)
}
}
- // update router state
+ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // Update `RouterTable`
+
for (Router &router : Get<RouterTable>())
{
uint32_t age;
@@ -1635,53 +1628,35 @@ void MleRouter::HandleTimeTick(void)
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)
- {
- LogInfo("Router timeout expired");
- RemoveNeighbor(router);
- continue;
- }
-
-#else
-
- if (age >= kMaxNeighborAge)
+#if OPENTHREAD_CONFIG_MLE_SEND_LINK_REQUEST_ON_ADV_TIMEOUT
+ if (age < kMaxNeighborAge + kMaxTxCount * kUnicastRetxDelay)
{
- if (age < kMaxNeighborAge + kMaxTxCount * kUnicastRetxDelay)
- {
- LogInfo("Router timeout expired");
- IgnoreError(SendLinkRequest(&router));
- }
- else
- {
- RemoveNeighbor(router);
- continue;
- }
+ LogInfo("No Adv from router 0x%04x - sending Link Request", router.GetRloc16());
+ IgnoreError(SendLinkRequest(&router));
}
-
+ 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 @@ Error MleRouter::ProcessAddressRegistrationTlv(RxInfo &aRxInfo, Child &aChild)
#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 @@ Error MleRouter::ProcessAddressRegistrationTlv(RxInfo &aRxInfo, Child &aChild)
#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 @@ Error MleRouter::ProcessAddressRegistrationTlv(RxInfo &aRxInfo, Child &aChild)
}
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 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo)
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 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo)
ExitNow(error = kErrorParse);
}
- // Active Timestamp
switch (Tlv::Find<ActiveTimestampTlv>(aRxInfo.mMessage, timestamp))
{
case kErrorNone:
@@ -2067,7 +2031,6 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo)
ExitNow(error = kErrorParse);
}
- // Pending Timestamp
switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, timestamp))
{
case kErrorNone:
@@ -2093,12 +2056,10 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo)
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 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo)
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 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo)
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 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo)
tlvList.Add(Tlv::kLinkFrameCounter);
}
- // IPv6 Address TLV
switch (ProcessAddressRegistrationTlv(aRxInfo, *child))
{
case kErrorNone:
@@ -2248,7 +2206,6 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo)
ExitNow(error = kErrorParse);
}
- // Leader Data
switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData))
{
case kErrorNone:
@@ -2260,7 +2217,6 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo)
ExitNow(error = kErrorParse);
}
- // Timeout
switch (Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout))
{
case kErrorNone:
@@ -2280,7 +2236,6 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo)
ExitNow(error = kErrorParse);
}
- // Supervision interval
switch (Tlv::Find<SupervisionIntervalTlv>(aRxInfo.mMessage, supervisionInterval))
{
case kErrorNone:
@@ -2298,7 +2253,6 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo)
child->SetSupervisionInterval(supervisionInterval);
- // TLV Request
switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList))
{
case kErrorNone:
@@ -2408,7 +2362,6 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo)
child = static_cast<Child *>(aRxInfo.mNeighbor);
- // Response
switch (aRxInfo.mMessage.ReadResponseTlv(response))
{
case kErrorNone:
@@ -2424,7 +2377,6 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo)
Log(kMessageReceive, kTypeChildUpdateResponseOfChild, aRxInfo.mMessageInfo.GetPeerAddr(), child->GetRloc16());
- // Source Address
switch (Tlv::Find<SourceAddressTlv>(aRxInfo.mMessage, sourceAddress))
{
case kErrorNone:
@@ -2443,7 +2395,6 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo)
ExitNow(error = kErrorParse);
}
- // Status
switch (Tlv::Find<StatusTlv>(aRxInfo.mMessage, status))
{
case kErrorNone:
@@ -2455,8 +2406,6 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo)
ExitNow(error = kErrorParse);
}
- // Link-Layer Frame Counter
-
switch (Tlv::Find<LinkFrameCounterTlv>(aRxInfo.mMessage, linkFrameCounter))
{
case kErrorNone:
@@ -2469,7 +2418,6 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo)
ExitNow(error = kErrorParse);
}
- // MLE Frame Counter
switch (Tlv::Find<MleFrameCounterTlv>(aRxInfo.mMessage, mleFrameCounter))
{
case kErrorNone:
@@ -2481,7 +2429,6 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo)
ExitNow(error = kErrorNone);
}
- // Timeout
switch (Tlv::Find<TimeoutTlv>(aRxInfo.mMessage, timeout))
{
case kErrorNone:
@@ -2508,7 +2455,6 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo)
}
}
- // IPv6 Address
switch (ProcessAddressRegistrationTlv(aRxInfo, *child))
{
case kErrorNone:
@@ -2518,7 +2464,6 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo)
ExitNow(error = kErrorParse);
}
- // Leader Data
switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData))
{
case kErrorNone:
@@ -2533,7 +2478,7 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo)
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 @@ void MleRouter::HandleDataRequest(RxInfo &aRxInfo)
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 @@ void MleRouter::HandleDataRequest(RxInfo &aRxInfo)
ExitNow(error = kErrorParse);
}
- // Pending Timestamp
switch (Tlv::Find<PendingTimestampTlv>(aRxInfo.mMessage, timestamp))
{
case kErrorNone:
@@ -2685,7 +2627,6 @@ void MleRouter::HandleDiscoveryRequest(RxInfo &aRxInfo)
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 @@ Error MleRouter::SendDiscoveryResponse(const Ip6::Address &aDestination, const M
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 @@ Error MleRouter::SendDiscoveryResponse(const Ip6::Address &aDestination, const M
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 @@ Error MleRouter::SendChildIdResponse(Child &aChild)
{
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 @@ Error MleRouter::SendChildIdResponse(Child &aChild)
} while (mChildTable.FindChild(rloc16, Child::kInStateAnyExceptInvalid) != nullptr);
- // allocate Child ID
aChild.SetRloc16(rloc16);
}
@@ -2937,14 +2873,16 @@ Error MleRouter::SendChildUpdateRequest(Child &aChild)
{
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 @@ void MleRouter::SendDataResponse(const Ip6::Address &aDestination,
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 @@ void MleRouter::RemoveNeighbor(Neighbor &aNeighbor)
if (aNeighbor.IsFullThreadDevice())
{
- // Clear all EID-to-RLOC entries associated with the child.
Get<AddressResolver>().RemoveEntriesForRloc16(aNeighbor.GetRloc16());
}
@@ -3291,11 +3226,9 @@ void MleRouter::ResolveRoutingLoops(uint16_t aSourceMac, uint16_t aDestRloc16)
ExitNow();
}
- // loop exists
router = mRouterTable.FindRouterByRloc16(aDestRloc16);
VerifyOrExit(router != nullptr);
- // invalidate next hop
router->SetNextHopToInvalid();
ResetAdvertiseInterval();
@@ -3305,46 +3238,39 @@ exit:
Error MleRouter::CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header)
{
- Error error = kErrorNone;
+ bool isReachable = false;
if (IsChild())
{
- error = Mle::CheckReachability(aMeshDest, aIp6Header);
- ExitNow();
- }
-
- if (aMeshDest == Get<Mac::Mac>().GetShortAddress())
- {
- // mesh destination is this device
- if (Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination()))
+ if (aMeshDest == GetRloc16())
{
- // IPv6 destination is this device
- ExitNow();
+ isReachable = Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination());
}
- else if (mNeighborTable.FindNeighbor(aIp6Header.GetDestination()) != nullptr)
+ else
{
- // IPv6 destination is an RFD child
- ExitNow();
+ isReachable = true;
}
+
+ ExitNow();
}
- else if (RouterIdFromRloc16(aMeshDest) == mRouterId)
+
+ if (aMeshDest == GetRloc16())
{
- // mesh destination is a child of this device
- if (mChildTable.FindChild(aMeshDest, Child::kInStateValidOrRestoring))
- {
- ExitNow();
- }
+ isReachable = Get<ThreadNetif>().HasUnicastAddress(aIp6Header.GetDestination()) ||
+ (mNeighborTable.FindNeighbor(aIp6Header.GetDestination()) != nullptr);
+ ExitNow();
}
- else if (GetNextHop(aMeshDest) != Mac::kShortAddrInvalid)
+
+ if (RouterIdFromRloc16(aMeshDest) == mRouterId)
{
- // forwarding to another router and route is known
+ isReachable = (mChildTable.FindChild(aMeshDest, Child::kInStateValidOrRestoring) != nullptr);
ExitNow();
}
- error = kErrorNoRoute;
+ isReachable = (GetNextHop(aMeshDest) != Mac::kShortAddrInvalid);
exit:
- return error;
+ return isReachable ? kErrorNone : kErrorNoRoute;
}
Error MleRouter::SendAddressSolicit(ThreadStatusTlv::Status aStatus)
@@ -3458,7 +3384,6 @@ void MleRouter::HandleAddressSolicitResponse(Coap::Message *aMessage,
SuccessOrExit(Tlv::FindTlv(*aMessage, routerMaskTlv));
VerifyOrExit(routerMaskTlv.IsValid());
- // assign short address
SetRouterId(routerId);
SetStateRouter(Rloc16FromRouterId(mRouterId));
@@ -3521,7 +3446,6 @@ void MleRouter::HandleAddressSolicitResponse(Coap::Message *aMessage,
}
exit:
- // Send announce after received address solicit reply if needed
InformPreviousChannel();
}
@@ -3582,7 +3506,6 @@ template <> void MleRouter::HandleTmf<kUriAddressSolicit>(Coap::Message &aMessag
}
#endif
- // Check if allocation already exists
router = mRouterTable.FindRouter(extAddress);
if (router != nullptr)
@@ -3674,7 +3597,10 @@ void MleRouter::SendAddressSolicitResponse(const Coap::Message &aRequest,
// 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 @@ void MleRouter::SendAddressSolicitResponse(const Coap::Message &aRequest,
oldRloc16 = aMessageInfo.GetPeerAddr().GetIid().GetLocator();
VerifyOrExit(oldRloc16 != aRouter->GetRloc16());
+ VerifyOrExit(!RouterIdMatch(oldRloc16, GetRloc16()));
Get<AddressResolver>().RemoveEntriesForRloc16(oldRloc16);
}
@@ -3760,13 +3687,11 @@ void MleRouter::FillConnectivityTlv(ConnectivityTlv &aTlv)
{
if (router.GetRloc16() == GetRloc16())
{
- // skip self
continue;
}
if (!router.IsStateValid())
{
- // skip non-neighbor routers
continue;
}
@@ -3966,20 +3891,6 @@ exit:
}
#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 65bfe114a..955674c42 100644
--- a/src/core/thread/mle_router.hpp
+++ b/src/core/thread/mle_router.hpp
@@ -649,9 +649,6 @@ private:
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 @@ private:
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 30ebe0038..28675ad6f 100644
--- a/src/core/thread/network_data.cpp
+++ b/src/core/thread/network_data.cpp
@@ -50,6 +50,9 @@ namespace NetworkData {
RegisterLogModule("NetworkData");
+//---------------------------------------------------------------------------------------------------------------------
+// NetworkData
+
Error NetworkData::CopyNetworkData(Type aType, uint8_t *aData, uint8_t &aDataLength) const
{
Error error;
@@ -402,153 +405,6 @@ exit:
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,226 +495,286 @@ exit:
return match;
}
-NetworkDataTlv *MutableNetworkData::AppendTlv(uint16_t aTlvSize)
+void NetworkData::FindRlocs(BorderRouterFilter aBrFilter, RoleFilter aRoleFilter, Rlocs &aRlocs) const
{
- NetworkDataTlv *tlv;
+ Iterator iterator = kIteratorInit;
+ OnMeshPrefixConfig prefix;
+ ExternalRouteConfig route;
+ ServiceConfig service;
+ Config config;
- VerifyOrExit(CanInsert(aTlvSize), tlv = nullptr);
+ aRlocs.Clear();
- tlv = GetTlvsEnd();
- mLength += static_cast<uint8_t>(aTlvSize);
+ 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 tlv;
+ return;
}
-void MutableNetworkData::Insert(void *aStart, uint8_t aLength)
+uint8_t NetworkData::CountBorderRouters(RoleFilter aRoleFilter) const
{
- uint8_t *start = reinterpret_cast<uint8_t *>(aStart);
+ Rlocs rlocs;
- OT_ASSERT(CanInsert(aLength) && mTlvs <= start && start <= mTlvs + mLength);
- memmove(start + aLength, start, mLength - static_cast<size_t>(start - mTlvs));
- mLength += aLength;
+ FindRlocs(kBrProvidingExternalIpConn, aRoleFilter, rlocs);
+
+ return rlocs.GetLength();
}
-void MutableNetworkData::Remove(void *aRemoveStart, uint8_t aRemoveLength)
+bool NetworkData::ContainsBorderRouterWithRloc(uint16_t aRloc16) const
{
- uint8_t *end = GetBytes() + mLength;
- uint8_t *removeStart = reinterpret_cast<uint8_t *>(aRemoveStart);
- uint8_t *removeEnd = removeStart + aRemoveLength;
+ Rlocs rlocs;
- OT_ASSERT((aRemoveLength <= mLength) && (GetBytes() <= removeStart) && (removeEnd <= end));
+ FindRlocs(kBrProvidingExternalIpConn, kAnyRole, rlocs);
- memmove(removeStart, removeEnd, static_cast<uint8_t>(end - removeEnd));
- mLength -= aRemoveLength;
+ return rlocs.Contains(aRloc16);
}
-void MutableNetworkData::RemoveTlv(NetworkDataTlv *aTlv) { Remove(aTlv, aTlv->GetSize()); }
-
-Error NetworkData::GetNextServer(Iterator &aIterator, uint16_t &aRloc16) const
+void NetworkData::AddRloc16ToRlocs(uint16_t aRloc16, Rlocs &aRlocs, RoleFilter aRoleFilter)
{
- Error error;
- OnMeshPrefixConfig prefixConfig;
- ExternalRouteConfig routeConfig;
- ServiceConfig serviceConfig;
- Config config;
-
- config.mOnMeshPrefix = &prefixConfig;
- config.mExternalRoute = &routeConfig;
- config.mService = &serviceConfig;
- config.mLowpanContext = nullptr;
+ switch (aRoleFilter)
+ {
+ case kAnyRole:
+ break;
- SuccessOrExit(error = Iterate(aIterator, Mac::kShortAddrBroadcast, config));
+ case kRouterRoleOnly:
+ VerifyOrExit(Mle::IsActiveRouter(aRloc16));
+ break;
- 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);
+ 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;
}
-Error NetworkData::FindBorderRouters(RoleFilter aRoleFilter, uint16_t aRlocs[], uint8_t &aRlocsLength) const
+//---------------------------------------------------------------------------------------------------------------------
+// MutableNetworkData
+
+void MutableNetworkData::RemoveTemporaryData(void)
{
- class Rlocs // Wrapper over an array of RLOC16s.
+ NetworkDataTlv *cur = GetTlvsStart();
+
+ while (cur < GetTlvsEnd())
{
- public:
- Rlocs(RoleFilter aRoleFilter, uint16_t *aRlocs, uint8_t aRlocsMaxLength)
- : mRoleFilter(aRoleFilter)
- , mRlocs(aRlocs)
- , mLength(0)
- , mMaxLength(aRlocsMaxLength)
+ bool shouldRemove = false;
+
+ switch (cur->GetType())
{
- }
+ case NetworkDataTlv::kTypePrefix:
+ shouldRemove = RemoveTemporaryDataIn(*As<PrefixTlv>(cur));
+ break;
- uint8_t GetLength(void) const { return mLength; }
+ case NetworkDataTlv::kTypeService:
+ shouldRemove = RemoveTemporaryDataIn(*As<ServiceTlv>(cur));
+ break;
- Error AddRloc16(uint16_t aRloc16)
+ default:
+ shouldRemove = !cur->IsStable();
+ break;
+ }
+
+ if (shouldRemove)
{
- // 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`.
+ RemoveTlv(cur);
+ continue;
+ }
+
+ cur = cur->GetNext();
+ }
+}
- Error error = kErrorNone;
- uint8_t index;
+bool MutableNetworkData::RemoveTemporaryDataIn(PrefixTlv &aPrefix)
+{
+ NetworkDataTlv *cur = aPrefix.GetSubTlvs();
- switch (mRoleFilter)
+ while (cur < aPrefix.GetNext())
+ {
+ if (cur->IsStable())
+ {
+ switch (cur->GetType())
{
- case kAnyRole:
- break;
+ case NetworkDataTlv::kTypeBorderRouter:
+ {
+ BorderRouterTlv *borderRouter = As<BorderRouterTlv>(cur);
+ ContextTlv *context = aPrefix.FindSubTlv<ContextTlv>();
- case kRouterRoleOnly:
- VerifyOrExit(Mle::IsActiveRouter(aRloc16));
- break;
+ // 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);
+ }
+ }
- case kChildRoleOnly:
- VerifyOrExit(!Mle::IsActiveRouter(aRloc16));
break;
}
- for (index = 0; index < mLength; index++)
+ case NetworkDataTlv::kTypeHasRoute:
{
- if (mRlocs[index] == aRloc16)
+ HasRouteTlv *hasRoute = As<HasRouteTlv>(cur);
+
+ // Replace r_border_router_16
+ for (HasRouteEntry *entry = hasRoute->GetFirstEntry(); entry <= hasRoute->GetLastEntry();
+ entry = entry->GetNext())
{
- break;
+ entry->SetRloc(0xfffe);
}
+
+ break;
}
- if (index == mLength)
- {
- VerifyOrExit(mLength < mMaxLength, error = kErrorNoBufs);
- mRlocs[mLength++] = aRloc16;
+ default:
+ break;
}
- exit:
- return error;
+ // keep stable tlv
+ cur = cur->GetNext();
+ }
+ else
+ {
+ // remove temporary tlv
+ uint8_t subTlvSize = cur->GetSize();
+ RemoveTlv(cur);
+ aPrefix.SetSubTlvsLength(aPrefix.GetSubTlvsLength() - subTlvSize);
}
+ }
- private:
- RoleFilter mRoleFilter;
- uint16_t *mRlocs;
- uint8_t mLength;
- uint8_t mMaxLength;
- };
+ return (aPrefix.GetSubTlvsLength() == 0);
+}
- Error error = kErrorNone;
- Rlocs rlocs(aRoleFilter, aRlocs, aRlocsLength);
- Iterator iterator = kIteratorInit;
- ExternalRouteConfig route;
- OnMeshPrefixConfig prefix;
+bool MutableNetworkData::RemoveTemporaryDataIn(ServiceTlv &aService)
+{
+ NetworkDataTlv *cur = aService.GetSubTlvs();
- while (GetNextExternalRoute(iterator, route) == kErrorNone)
+ while (cur < aService.GetNext())
{
- SuccessOrExit(error = rlocs.AddRloc16(route.mRloc16));
- }
+ if (cur->IsStable())
+ {
+ switch (cur->GetType())
+ {
+ case NetworkDataTlv::kTypeServer:
+ As<ServerTlv>(cur)->SetServer16(Mle::ServiceAlocFromId(aService.GetServiceId()));
+ break;
- iterator = kIteratorInit;
+ default:
+ break;
+ }
- while (GetNextOnMeshPrefix(iterator, prefix) == kErrorNone)
- {
- if (!prefix.mDefaultRoute || !prefix.mOnMesh)
+ // keep stable tlv
+ cur = cur->GetNext();
+ }
+ else
{
- continue;
+ // remove temporary tlv
+ uint8_t subTlvSize = cur->GetSize();
+ RemoveTlv(cur);
+ aService.SetSubTlvsLength(aService.GetSubTlvsLength() - subTlvSize);
}
-
- SuccessOrExit(error = rlocs.AddRloc16(prefix.mRloc16));
}
-exit:
- aRlocsLength = rlocs.GetLength();
- return error;
+ return (aService.GetSubTlvsLength() == 0);
}
-uint8_t NetworkData::CountBorderRouters(RoleFilter aRoleFilter) const
+NetworkDataTlv *MutableNetworkData::AppendTlv(uint16_t aTlvSize)
{
- // 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;
+ NetworkDataTlv *tlv;
- uint16_t rlocs[kMaxRlocs];
- uint8_t rlocsLength = kMaxRlocs;
+ VerifyOrExit(CanInsert(aTlvSize), tlv = nullptr);
- SuccessOrAssert(FindBorderRouters(aRoleFilter, rlocs, rlocsLength));
+ tlv = GetTlvsEnd();
+ mLength += static_cast<uint8_t>(aTlvSize);
- return rlocsLength;
+exit:
+ return tlv;
}
-bool NetworkData::ContainsBorderRouterWithRloc(uint16_t aRloc16) const
+void MutableNetworkData::Insert(void *aStart, uint8_t aLength)
{
- 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);
- }
- }
+ uint8_t *start = reinterpret_cast<uint8_t *>(aStart);
-exit:
- return contains;
+ OT_ASSERT(CanInsert(aLength) && mTlvs <= start && start <= mTlvs + mLength);
+ memmove(start + aLength, start, mLength - static_cast<size_t>(start - mTlvs));
+ mLength += aLength;
}
-Error NetworkData::FindDomainIdFor(const Ip6::Prefix &aPrefix, uint8_t &aDomainId) const
+void MutableNetworkData::Remove(void *aRemoveStart, uint8_t aRemoveLength)
{
- Error error = kErrorNone;
- const PrefixTlv *prefixTlv = FindPrefix(aPrefix);
+ uint8_t *end = GetBytes() + mLength;
+ uint8_t *removeStart = reinterpret_cast<uint8_t *>(aRemoveStart);
+ uint8_t *removeEnd = removeStart + aRemoveLength;
- VerifyOrExit(prefixTlv != nullptr, error = kErrorNotFound);
- aDomainId = prefixTlv->GetDomainId();
+ OT_ASSERT((aRemoveLength <= mLength) && (GetBytes() <= removeStart) && (removeEnd <= end));
-exit:
- return error;
+ memmove(removeStart, removeEnd, static_cast<uint8_t>(end - removeEnd));
+ mLength -= aRemoveLength;
}
+void MutableNetworkData::RemoveTlv(NetworkDataTlv *aTlv) { Remove(aTlv, aTlv->GetSize()); }
+
} // namespace NetworkData
} // namespace ot
diff --git a/src/core/thread/network_data.hpp b/src/core/thread/network_data.hpp
index 17c515142..d1d8270c8 100644
--- a/src/core/thread/network_data.hpp
+++ b/src/core/thread/network_data.hpp
@@ -325,18 +325,6 @@ public:
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 @@ public:
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 @@ private:
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 @@ protected:
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 fe423ef4f..5cfc44e56 100644
--- a/src/core/thread/network_data_leader_ftd.cpp
+++ b/src/core/thread/network_data_leader_ftd.cpp
@@ -680,10 +680,7 @@ exit:
if (!mIsClone)
#endif
{
- if (error != kErrorNone)
- {
- LogNote("Failed to register network data: %s", ErrorToString(error));
- }
+ LogWarnOnError(error, "register network data");
}
}
@@ -1218,10 +1215,10 @@ void Leader::HandleNetworkDataRestoredAfterReset(void)
{
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 @@ void Leader::HandleNetworkDataRestoredAfterReset(void)
// got the chance to send the updated Network Data to other
// routers.
- while (GetNextServer(iterator, rloc16) == kErrorNone)
+ FindRlocs(kAnyBrOrServer, kAnyRole, rlocs);
+
+ for (uint16_t rloc : rlocs)
{
- if (!Get<RouterTable>().IsAllocated(Mle::RouterIdFromRloc16(rloc16)))
+ if (!Get<RouterTable>().IsAllocated(Mle::RouterIdFromRloc16(rloc)))
{
- // 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.
-
- RemoveRloc(rloc16, kMatchModeRouterId, flags);
- iterator = kIteratorInit;
+ RemoveRloc(rloc, kMatchModeRouterId, flags);
}
}
diff --git a/src/core/thread/network_data_notifier.cpp b/src/core/thread/network_data_notifier.cpp
index 750b3799b..e451a73e4 100644
--- a/src/core/thread/network_data_notifier.cpp
+++ b/src/core/thread/network_data_notifier.cpp
@@ -129,13 +129,14 @@ Error Notifier::RemoveStaleChildEntries(void)
// - `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 ec925b824..a64f0c39e 100644
--- a/src/core/thread/network_data_publisher.cpp
+++ b/src/core/thread/network_data_publisher.cpp
@@ -661,16 +661,17 @@ void Publisher::DnsSrpServiceEntry::Process(void)
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 @@ void Publisher::DnsSrpServiceEntry::Process(void)
case kTypeUnicast:
desiredNumEntries = kDesiredNumUnicast;
- CountUnicastEntries(numEntries, numPreferredEntries);
+ CountServiceDataUnicastEntries(numEntries, numPreferredEntries);
break;
}
@@ -721,14 +722,19 @@ void Publisher::DnsSrpServiceEntry::CountAnycastEntries(uint8_t &aNumEntries, ui
}
}
-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
- // the Network Data.
+ // 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)) !=
@@ -737,45 +743,57 @@ void Publisher::DnsSrpServiceEntry::CountUnicastEntries(uint8_t &aNumEntries, ui
TlvIterator subTlvIterator(*serviceTlv);
const ServerTlv *serverSubTlv;
+ if (serviceTlv->GetServiceDataLength() >= sizeof(Service::DnsSrpUnicast::ServiceData))
+ {
+ aHasServiceDataEntry = true;
+ }
+
while (((serverSubTlv = subTlvIterator.Iterate<ServerTlv>())) != nullptr)
{
- if (serviceTlv->GetServiceDataLength() >= sizeof(Service::DnsSrpUnicast::ServiceData))
+ if (serverSubTlv->GetServerDataLength() < sizeof(Service::DnsSrpUnicast::ServerData))
{
- 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++;
- }
+ continue;
}
- if (serverSubTlv->GetServerDataLength() >= sizeof(Service::DnsSrpUnicast::ServerData))
+ aNumEntries++;
+
+ if (IsPreferred(serverSubTlv->GetServer16()))
{
- aNumEntries++;
+ 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;
+ ServiceData data;
+
+ data.InitFrom(Service::DnsSrpUnicast::kServiceData);
+
+ while ((serviceTlv = Get<Leader>().FindNextThreadService(serviceTlv, data, NetworkData::kServicePrefixMatch)) !=
+ nullptr)
+ {
+ TlvIterator subTlvIterator(*serviceTlv);
+ const ServerTlv *serverSubTlv;
- // If our entry also uses the server TLV data (with
- // ML-EID address), then the we prefer based on the
- // associated RLOC16.
+ if (serviceTlv->GetServiceDataLength() < sizeof(Service::DnsSrpUnicast::ServiceData))
+ {
+ continue;
+ }
- if ((GetType() == kTypeUnicastMeshLocalEid) && IsPreferred(serverSubTlv->GetServer16()))
- {
- aNumPreferredEntries++;
- }
+ while (((serverSubTlv = subTlvIterator.Iterate<ServerTlv>())) != nullptr)
+ {
+ aNumEntries++;
+
+ if (IsPreferred(serverSubTlv->GetServer16()))
+ {
+ aNumPreferredEntries++;
}
}
}
diff --git a/src/core/thread/network_data_publisher.hpp b/src/core/thread/network_data_publisher.hpp
index 3ac2c825a..4503efb1e 100644
--- a/src/core/thread/network_data_publisher.hpp
+++ b/src/core/thread/network_data_publisher.hpp
@@ -437,7 +437,10 @@ private:
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 1457aa239..af63ff5d7 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 @@ enum RoleFilter : uint8_t
};
/**
+ * 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 cfa139961..59dd59131 100644
--- a/src/core/thread/panid_query_server.cpp
+++ b/src/core/thread/panid_query_server.cpp
@@ -124,7 +124,7 @@ void PanIdQueryServer::SendConflict(void)
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 c3584aad3..0277ede49 100644
--- a/src/core/thread/version.hpp
+++ b/src/core/thread/version.hpp
@@ -42,10 +42,12 @@ namespace ot {
constexpr uint16_t kThreadVersion = OPENTHREAD_CONFIG_THREAD_VERSION; ///< Thread Version of this device.
-constexpr uint16_t kThreadVersion1p1 = OT_THREAD_VERSION_1_1; ///< Thread Version 1.1
-constexpr uint16_t kThreadVersion1p2 = OT_THREAD_VERSION_1_2; ///< Thread Version 1.2
-constexpr uint16_t kThreadVersion1p3 = OT_THREAD_VERSION_1_3; ///< Thread Version 1.3
+constexpr uint16_t 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 7ed7df486..788a077ca 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 @@ ChannelManager::ChannelManager(Instance &aInstance)
: 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 @@ void ChannelManager::RequestChannelChange(uint8_t aChannel)
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 @@ void ChannelManager::HandleDatasetUpdateDone(Error aError)
mState = kStateIdle;
StartAutoSelectTimer();
}
+#endif // OPENTHREAD_FTD
void ChannelManager::HandleTimer(void)
{
@@ -160,12 +217,14 @@ void ChannelManager::HandleTimer(void)
{
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 @@ bool ChannelManager::ShouldAttemptChannelChange(void)
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 @@ Error ChannelManager::RequestChannelSelect(bool aSkipQualityCheck)
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 @@ Error ChannelManager::RequestChannelSelect(bool aSkipQualityCheck)
(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 @@ void ChannelManager::StartAutoSelectTimer(void)
{
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 @@ exit:
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 @@ Error ChannelManager::SetAutoChannelSelectionInterval(uint32_t aInterval)
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 ad46141e8..02d93053b 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 @@ namespace Utils {
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 @@ public:
*/
explicit ChannelManager(Instance &aInstance);
+#if OPENTHREAD_FTD
/**
* Requests a Thread network channel change.
*
@@ -91,16 +96,18 @@ public:
* @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 @@ public:
*
*/
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 @@ public:
* (@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 @@ public:
* @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 @@ public:
* @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 @@ public:
* @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 @@ private:
// 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 @@ private:
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 f19185fb6..a130ca3a0 100644
--- a/src/core/utils/otns.cpp
+++ b/src/core/utils/otns.cpp
@@ -166,11 +166,9 @@ void Otns::EmitCoapSend(const Coap::Message &aMessage, const Ip6::MessageInfo &a
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 @@ void Otns::EmitCoapReceive(const Coap::Message &aMessage, const Ip6::MessageInfo
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 @@ void Otns::EmitCoapSendFailure(Error aError, Coap::Message &aMessage, const Ip6:
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 52f568133..56e75da93 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 f03d945dd..e3911c085 100644
--- a/src/lib/spinel/BUILD.gn
+++ b/src/lib/spinel/BUILD.gn
@@ -34,6 +34,8 @@ declare_args() {
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 b8be41c82..1c21d6e84 100644
--- a/src/lib/spinel/CMakeLists.txt
+++ b/src/lib/spinel/CMakeLists.txt
@@ -89,7 +89,11 @@ target_include_directories(openthread-radio-spinel PUBLIC ${OT_PUBLIC_INCLUDES}
target_include_directories(openthread-spinel-ncp PUBLIC ${OT_PUBLIC_INCLUDES} PRIVATE ${COMMON_INCLUDES})
target_include_directories(openthread-spinel-rcp PUBLIC ${OT_PUBLIC_INCLUDES} PRIVATE ${COMMON_INCLUDES})
-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 000000000..46636c7f1
--- /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 000000000..ea86f4d7c
--- /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 ab1ef835b..61945a5ca 100644
--- a/src/lib/spinel/multi_frame_buffer.hpp
+++ b/src/lib/spinel/multi_frame_buffer.hpp
@@ -371,7 +371,7 @@ public:
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 b41fde342..b250f09ca 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 @@ exit:
}
RadioSpinel::RadioSpinel(void)
- : mInstance(nullptr)
+ : Logger("RadioSpinel")
+ , mInstance(nullptr)
, mSpinelInterface(nullptr)
, mCmdTidsInUse(0)
, mCmdNextTid(1)
@@ -670,6 +672,9 @@ void RadioSpinel::HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer,
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 @@ exit:
}
#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 @@ otError RadioSpinel::SpinelStatusToOtError(spinel_status_t aStatus)
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 7cdd1f8de..5f55c7588 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 @@ struct RadioSpinelCallbacks
* co-processor(RCP).
*
*/
-class RadioSpinel
+class RadioSpinel : private Logger
{
public:
/**
@@ -1238,17 +1239,6 @@ private:
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 @@ private:
#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 91a9ec36c..6cd390486 100644
--- a/src/lib/spinel/spinel_encoder.cpp
+++ b/src/lib/spinel/spinel_encoder.cpp
@@ -291,5 +291,7 @@ exit:
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 6b9ea983d..c678519b4 100644
--- a/src/lib/spinel/spinel_encoder.hpp
+++ b/src/lib/spinel/spinel_encoder.hpp
@@ -676,6 +676,12 @@ public:
*/
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 e0b4c1881..a1ad3adb3 100644
--- a/src/ncp/ncp_base.cpp
+++ b/src/ncp/ncp_base.cpp
@@ -1285,6 +1285,8 @@ otError NcpBase::CommandHandler_RESET(uint8_t aHeader)
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 badc6a5e4..aa87efa51 100644
--- a/src/ncp/ncp_base_mtd.cpp
+++ b/src/ncp/ncp_base_mtd.cpp
@@ -690,7 +690,7 @@ template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_KEY_SWITCH_GUARDT
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 51d16ca0e..1590ed284 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 @@ private:
} // 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 07f120840..6311bdcb1 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 @@ otError Configuration::SetRegion(uint16_t aRegionCode)
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 @@ otError Configuration::GetDomain(uint16_t aRegionCode, Power::Domain &aDomain)
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 @@ otError Configuration::UpdateChannelMasks(const Power::Domain &aDomain)
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 @@ otError Configuration::UpdateTargetPower(const Power::Domain &aDomain)
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 @@ otError Configuration::UpdateTargetPower(const Power::Domain &aDomain)
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 @@ otError Configuration::UpdateCalibratedPower(void)
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 @@ otError Configuration::UpdateCalibratedPower(void)
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 @@ otError Configuration::GetNextTargetPower(const Power::Domain &aDomain,
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 71c479ddb..e2a294294 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 @@ namespace Posix {
* 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 @@ private:
} // 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 14c40cf8a..1436e4516 100644
--- a/src/posix/platform/daemon.cpp
+++ b/src/posix/platform/daemon.cpp
@@ -75,6 +75,8 @@ void GetFilename(Filename &aFilename, const char *aPattern)
} // namespace
+const char Daemon::kLogModuleName[] = "Daemon";
+
int Daemon::OutputFormat(const char *aFormat, ...)
{
int ret;
@@ -97,7 +99,7 @@ int Daemon::OutputFormatV(const char *aFormat, va_list aArguments)
"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 @@ int Daemon::OutputFormatV(const char *aFormat, va_list aArguments)
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 @@ void Daemon::InitializeSessionSocket(void)
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 @@ exit:
}
else
{
- otLogInfoPlat("Session socket is ready");
+ LogInfo("Session socket is ready");
}
}
@@ -318,7 +320,7 @@ void Daemon::TearDown(void)
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 @@ void Daemon::Process(const otSysMainloopContext &aContext)
{
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 0d4051136..be3738b8f 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 19ad8c519..7e9d47eae 100644
--- a/src/posix/platform/firewall.cpp
+++ b/src/posix/platform/firewall.cpp
@@ -124,7 +124,7 @@ void UpdateIpSets(otInstance *aInstance)
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 d2c45e433..12726fc72 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 @@ otError HdlcInterface::Init(ReceiveFrameCallback aCallback, void *aCallbackConte
#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 @@ void HdlcInterface::HandleHdlcFrame(otError aError)
{
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 @@ otError HdlcInterface::ResetConnection(void)
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 6078135d5..c6e01c7b2 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 @@ namespace Posix {
* 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 @@ private:
} // 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 6e16b3f7f..0c9785228 100644
--- a/src/posix/platform/infra_if.cpp
+++ b/src/posix/platform/infra_if.cpp
@@ -128,6 +128,8 @@ void otSysCountInfraNetifAddresses(otSysInfraNetIfAddressCounters *aAddressCount
namespace ot {
namespace Posix {
+const char InfraNetif::kLogModuleName[] = "InfraNetif";
+
int InfraNetif::CreateIcmp6Socket(const char *aInfraIfName)
{
int sock;
@@ -174,7 +176,7 @@ int InfraNetif::CreateIcmp6Socket(const char *aInfraIfName)
#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 IsAddressUniqueLocal(const in6_addr &aAddress) { return (aAddress.s6_addr[0
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 @@ int CreateNetLinkSocket(void)
return sock;
}
+#endif // #ifdef __linux__
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
otError InfraNetif::SendIcmp6Nd(uint32_t aInfraIfIndex,
@@ -269,15 +273,16 @@ otError InfraNetif::SendIcmp6Nd(uint32_t aInfraIfIndex,
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 @@ uint32_t InfraNetif::GetFlags(void) const
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 @@ void InfraNetif::CountAddresses(otSysInfraNetIfAddressCounters &aAddressCounters
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 @@ bool InfraNetif::HasLinkLocalAddress(void) const
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 @@ bool InfraNetif::HasLinkLocalAddress(void) const
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 @@ void InfraNetif::SetInfraNetif(const char *aIfName, int aIcmp6Socket)
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 @@ void InfraNetif::SetInfraNetif(const char *aIfName, int aIcmp6Socket)
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 @@ void InfraNetif::SetInfraNetif(const char *aIfName, int aIcmp6Socket)
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 @@ exit:
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 @@ void InfraNetif::SetUp(void)
#endif
Mainloop::Manager::Get().Add(*this);
+
+ ExitNow(); // To silence unused `exit` label warning.
+
exit:
return;
}
@@ -488,11 +505,13 @@ void InfraNetif::Deinit(void)
}
#endif
+#ifdef __linux__
if (mNetLinkSocket != -1)
{
close(mNetLinkSocket);
mNetLinkSocket = -1;
}
+#endif
mInfraIfName[0] = '\0';
mInfraIfIndex = 0;
@@ -500,7 +519,9 @@ void InfraNetif::Deinit(void)
void InfraNetif::Update(otSysMainloopContext &aContext)
{
+#ifdef __linux__
VerifyOrExit(mNetLinkSocket != -1);
+#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
VerifyOrExit(mInfraIfIcmp6Socket != -1);
@@ -509,13 +530,17 @@ void InfraNetif::Update(otSysMainloopContext &aContext)
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 @@ void InfraNetif::ReceiveNetLinkMessage(void)
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 @@ void InfraNetif::ReceiveNetLinkMessage(void)
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 @@ exit:
return;
}
+#endif // #ifdef __linux__
+
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
void InfraNetif::ReceiveIcmp6Message(void)
{
@@ -599,7 +626,7 @@ void InfraNetif::ReceiveIcmp6Message(void)
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 @@ void InfraNetif::ReceiveIcmp6Message(void)
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 @@ void InfraNetif::DiscoverNat64PrefixDone(union sigval sv)
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 @@ otError InfraNetif::DiscoverNat64Prefix(uint32_t aInfraIfIndex)
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 @@ void InfraNetif::Process(const otSysMainloopContext &aContext)
#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 @@ void InfraNetif::Process(const otSysMainloopContext &aContext)
}
#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 e8aedd2f9..3eee24821 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 @@ namespace Posix {
* 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 @@ private:
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 @@ private:
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 @@ private:
} // 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 39c7bc168..841c317be 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 @@ private:
} // 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 000000000..e907b87cb
--- /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 6e11e5645..a9ce981c4 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 a224ae1ab..864f984d4 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 @@ void MulticastRoutingManager::Enable(void)
InitMulticastRouterSock();
- LogResult(OT_ERROR_NONE, "MulticastRoutingManager: %s", __FUNCTION__);
+ LogResult(OT_ERROR_NONE, "%s", __FUNCTION__);
exit:
return;
}
@@ -123,7 +125,7 @@ void MulticastRoutingManager::Disable(void)
{
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 @@ void MulticastRoutingManager::Add(const Ip6::Address &aAddress)
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 @@ void MulticastRoutingManager::Remove(const Ip6::Address &aAddress)
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 @@ void MulticastRoutingManager::UpdateMldReport(const Ip6::Address &aAddress, bool
? 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 @@ void MulticastRoutingManager::ProcessMulticastRouterMessages(void)
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 @@ otError MulticastRoutingManager::AddMulticastForwardingCache(const Ip6::Address
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 @@ void MulticastRoutingManager::UnblockInboundMulticastForwardingCache(const Ip6::
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 @@ bool MulticastRoutingManager::UpdateMulticastRouteInfo(MulticastForwardingCache
{
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 @@ bool MulticastRoutingManager::UpdateMulticastRouteInfo(MulticastForwardingCache
}
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 @@ const char *MulticastRoutingManager::MifIndexToString(MifIndex aMif)
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 @@ void MulticastRoutingManager::RemoveMulticastForwardingCache(
? 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 c4d7e32fd..9d4e0491e 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 d3b302497..e0a4c49b8 100644
--- a/src/posix/platform/netif.cpp
+++ b/src/posix/platform/netif.cpp
@@ -147,14 +147,14 @@ extern int
#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 @@ const char *otSysGetThreadNetifName(void) { return gNetifName; }
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 @@ using namespace ot::Posix::Ip6Utils;
#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 @@ static bool sIsSyncingState = false;
#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 @@ static void UpdateUnicastLinux(otInstance *aInstance, const otIp6AddressInfo &aA
#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 @@ static void UpdateUnicastLinux(otInstance *aInstance, const otIp6AddressInfo &aA
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 @@ static void UpdateUnicast(otInstance *aInstance, const otIp6AddressInfo &aAddres
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 @@ static void UpdateMulticast(otInstance *aInstance, const otIp6Address &aAddress,
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 @@ static void SetLinkState(otInstance *aInstance, bool aState)
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 @@ static void SetLinkState(otInstance *aInstance, bool aState)
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 @@ static void UpdateOmrRoutes(otInstance *aInstance)
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 @@ static void UpdateOmrRoutes(otInstance *aInstance)
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 @@ static void UpdateExternalRoutes(otInstance *aInstance)
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 @@ static void UpdateExternalRoutes(otInstance *aInstance)
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 @@ static void processNat64StateChange(void)
{
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 @@ static void processReceive(otMessage *aMessage, void *aContext)
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 @@ exit:
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 @@ static otError tryProcessIcmp6RaMessage(otInstance *aInstance, const uint8_t *da
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 @@ static void processTransmit(otInstance *aInstance)
}
#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 @@ exit:
{
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 @@ static void processNetifAddrEvent(otInstance *aInstance, struct nlmsghdr *aNetli
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 @@ static void processNetifAddrEvent(otInstance *aInstance, struct nlmsghdr *aNetli
// 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 @@ static void processNetifAddrEvent(otInstance *aInstance, struct nlmsghdr *aNetli
}
logAddrEvent(/* isAdd */ true, addr, error);
+
if (error == OT_ERROR_ALREADY || error == OT_ERROR_REJECTED)
{
error = OT_ERROR_NONE;
@@ -1292,7 +1361,7 @@ static void processNetifAddrEvent(otInstance *aInstance, struct nlmsghdr *aNetli
}
else if (aNetlinkMessage->nlmsg_type == RTM_DELADDR)
{
- if (!addr.IsMulticast())
+ if (!IsIp6AddressMulticast(addr))
{
error = otIp6RemoveUnicastAddress(aInstance, &addr);
}
@@ -1302,6 +1371,7 @@ static void processNetifAddrEvent(otInstance *aInstance, struct nlmsghdr *aNetli
}
logAddrEvent(/* isAdd */ false, addr, error);
+
if (error == OT_ERROR_NOT_FOUND || error == OT_ERROR_REJECTED)
{
error = OT_ERROR_NONE;
@@ -1317,7 +1387,7 @@ static void processNetifAddrEvent(otInstance *aInstance, struct nlmsghdr *aNetli
}
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 @@ static void processNetifAddrEvent(otInstance *aInstance, struct nlmsghdr *aNetli
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 @@ static void processNetifLinkEvent(otInstance *aInstance, struct nlmsghdr *aNetli
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 @@ static void processNetifLinkEvent(otInstance *aInstance, struct nlmsghdr *aNetli
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 @@ static void processNetifLinkEvent(otInstance *aInstance, struct nlmsghdr *aNetli
// 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 @@ static void processNetifLinkEvent(otInstance *aInstance, struct nlmsghdr *aNetli
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 @@ static void processNetifAddrEvent(otInstance *aInstance, struct rt_msghdr *rtm)
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 @@ static void processNetifAddrEvent(otInstance *aInstance, struct rt_msghdr *rtm)
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 @@ static void processNetifAddrEvent(otInstance *aInstance, struct rt_msghdr *rtm)
#endif
)
{
- if (!addr.IsMulticast())
+ if (!IsIp6AddressMulticast(addr))
{
otNetifAddress netAddr;
@@ -1519,16 +1591,14 @@ static void processNetifAddrEvent(otInstance *aInstance, struct rt_msghdr *rtm)
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 @@ static void processNetifAddrEvent(otInstance *aInstance, struct rt_msghdr *rtm)
{
error = otIp6AddUnicastAddress(aInstance, &netAddr);
logAddrEvent(/* isAdd */ true, addr, error);
+
if (error == OT_ERROR_ALREADY)
{
error = OT_ERROR_NONE;
@@ -1551,6 +1622,7 @@ static void processNetifAddrEvent(otInstance *aInstance, struct rt_msghdr *rtm)
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 @@ static void processNetifAddrEvent(otInstance *aInstance, struct rt_msghdr *rtm)
#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 @@ static void processNetifAddrEvent(otInstance *aInstance, struct rt_msghdr *rtm)
{
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 @@ static void processNetifInfoEvent(otInstance *aInstance, struct rt_msghdr *rtm)
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 @@ static void HandleNetlinkResponse(struct nlmsghdr *msg)
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 @@ static void HandleNetlinkResponse(struct nlmsghdr *msg)
if (err->error == 0)
{
- otLogInfoPlat("[netif] Succeeded to process request#%u", requestSeq);
+ LogInfo("Succeeded to process request#%u", requestSeq);
ExitNow();
}
@@ -1671,11 +1746,11 @@ static void HandleNetlinkResponse(struct nlmsghdr *msg)
}
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 @@ static void processNetlinkEvent(otInstance *aInstance)
// 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 @@ static void processNetlinkEvent(otInstance *aInstance)
#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 @@ static void processMLDEvent(otInstance *aInstance)
{
MLDv2Record *record = reinterpret_cast<MLDv2Record *>(&buffer[offset]);
- otError err;
- ot::Ip6::Address address;
+ otError err;
+ otIp6Address address;
+
+ ReadIp6AddressFrom(&record->mMulticastAddress, address);
- memcpy(&address.mFields.m8, &record->mMulticastAddress, sizeof(address.mFields.m8));
inet_ntop(AF_INET6, &record->mMulticastAddress, addressString, sizeof(addressString));
+
if (record->mRecordType == kICMPv6MLDv2RecordChangeToIncludeType)
{
err = otIp6SubscribeMulticastAddress(aInstance, &address);
@@ -1912,11 +1989,11 @@ static void SetAddrGenModeToNone(void)
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 @@ static void platformConfigureTunDevice(otPlatformConfig *aPlatformConfig)
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 @@ static void platformConfigureNetLink(void)
#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 @@ static void platformConfigureNetLink(void)
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 @@ void nat64Init(void)
{
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 f39c6f2ac..ade8ab2c6 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 912ceb590..a5d3a302d 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 @@ void platformBacktraceInit(void);
#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 4edbcb2d5..f4481f152 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 @@ private:
};
} // 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 8828f9bee..f5eb17cc2 100644
--- a/src/posix/platform/radio.cpp
+++ b/src/posix/platform/radio.cpp
@@ -57,6 +57,8 @@ namespace {
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 @@ void Radio::Init(const char *aUrl)
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 @@ Spinel::SpinelInterface *Radio::CreateSpinelInterface(const char *aInterfaceName
#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 @@ void Radio::ProcessRadioUrl(const RadioUrl &aRadioUrl)
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 @@ void Radio::ProcessMaxPowerTable(const RadioUrl &aRadioUrl)
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 @@ void Radio::ProcessMaxPowerTable(const RadioUrl &aRadioUrl)
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 49cdfdf3d..e4a8b1a9e 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 @@ namespace Posix {
* 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 @@ private:
} // 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 02460705b..0e088cb90 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 @@ private:
} // 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 c8d80e294..642d77137 100644
--- a/src/posix/platform/resolver.cpp
+++ b/src/posix/platform/resolver.cpp
@@ -61,6 +61,8 @@ extern ot::Posix::Resolver gResolver;
namespace ot {
namespace Posix {
+const char Resolver::kLogModuleName[] = "Resolver";
+
void Resolver::Init(void)
{
memset(mUpstreamTransaction, 0, sizeof(mUpstreamTransaction));
@@ -95,8 +97,7 @@ void Resolver::LoadDnsServerListFromConf(void)
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 @@ void Resolver::LoadDnsServerListFromConf(void)
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 @@ void Resolver::Query(otPlatDnsUpstreamQuery *aTxn, const otMessage *aQuery)
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 @@ Resolver::Transaction *Resolver::AllocateTransaction(otPlatDnsUpstreamQuery *aTh
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 @@ void Resolver::ForwardResponse(Transaction *aTxn)
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 8446a4763..fae0cf9be 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 @@ public:
/**
* 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 @@ public:
/**
* 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 @@ private:
#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 d2009aa37..bf2cabd6d 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 @@ void PlatformSettingsGetSensitiveKeys(otInstance *aInstance, const uint16_t **aK
} // 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 167f4b9c3..9d7860b34 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 @@ otError SpiInterface::Init(ReceiveFrameCallback aCallback, void *aCallbackContex
}
else
{
- otLogNotePlat("SPI interface enters polling mode.");
+ LogNote("SPI interface enters polling mode.");
}
InitResetPin(spiGpioResetDevice, spiGpioResetLine);
@@ -254,7 +256,7 @@ void SpiInterface::InitResetPin(const char *aCharDev, uint8_t aLine)
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 @@ void SpiInterface::InitIntPin(const char *aCharDev, uint8_t aLine)
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 @@ void SpiInterface::InitSpiDev(const char *aPath, uint8_t aMode, uint32_t aSpeed)
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 @@ void SpiInterface::TriggerReset(void)
// 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 @@ otError SpiInterface::PushPullSpi(void)
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 @@ otError SpiInterface::PushPullSpi(void)
{
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 @@ otError SpiInterface::PushPullSpi(void)
// 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 @@ otError SpiInterface::PushPullSpi(void)
// 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 @@ otError SpiInterface::PushPullSpi(void)
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 @@ otError SpiInterface::PushPullSpi(void)
{
mSlaveResetCount++;
- otLogNotePlat("Slave did reset (%" PRIu64 " resets so far)", mSlaveResetCount);
+ LogNote("Slave did reset (%" PRIu64 " resets so far)", mSlaveResetCount);
LogStats();
}
@@ -634,7 +636,7 @@ void SpiInterface::UpdateFdSet(void *aMainloopContext)
// 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 @@ void SpiInterface::UpdateFdSet(void *aMainloopContext)
{
// 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 @@ void SpiInterface::UpdateFdSet(void *aMainloopContext)
{
// 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 @@ void SpiInterface::Process(const void *aMainloopContext)
{
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 @@ exit:
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 095214a79..94b55074a 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 @@ namespace Posix {
* 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 @@ private:
} // 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 ea06bca88..6b6e6c086 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 sInitialized = false;
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 @@ static void PrepareSocket(uint16_t &aUdpPort)
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 @@ static void PrepareSocket(uint16_t &aUdpPort)
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 @@ static void PrepareSocket(uint16_t &aUdpPort)
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 @@ static otError SendPacket(const uint8_t *aBuffer, uint16_t aLength, const otSock
if (ret != aLength)
{
- otLogDebgPlat("[trel] SendPacket() -- sendto() failed errno %d", errno);
+ LogDebg("SendPacket() -- sendto() failed errno %d", errno);
switch (errno)
{
@@ -194,8 +242,8 @@ static otError SendPacket(const uint8_t *aBuffer, uint16_t aLength, const otSock
}
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 @@ static void ReceivePacket(int aSocket, otInstance *aInstance)
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 @@ static void SendQueuedPackets(void)
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 @@ static void EnqueuePacket(const uint8_t *aBuffer, uint16_t aLength, const otSock
// 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 @@ static void EnqueuePacket(const uint8_t *aBuffer, uint16_t aLength, const otSock
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 otPlatTrelResetCounters(otInstance *aInstance)
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 @@ void platformTrelDeinit(void)
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 4161672eb..89c6d3d8e 100644
--- a/src/posix/platform/udp.cpp
+++ b/src/posix/platform/udp.cpp
@@ -69,10 +69,6 @@ void *FdToHandle(int aFd) { return reinterpret_cast<void *>(aFd); }
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 @@ otError transmitPacket(int aFd, uint8_t *aPayload, uint16_t aLength, const otMes
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 @@ otError transmitPacket(int aFd, uint8_t *aPayload, uint16_t aLength, const otMes
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 @@ otError transmitPacket(int aFd, uint8_t *aPayload, uint16_t aLength, const otMes
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 @@ otError receivePacket(int aFd, uint8_t *aPayload, uint16_t &aLength, otMessageIn
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 @@ otError otPlatUdpBind(otUdpSocket *aUdpSocket)
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 @@ otError otPlatUdpBind(otUdpSocket *aUdpSocket)
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 @@ otError otPlatUdpBindToNetif(otUdpSocket *aUdpSocket, otNetifIdentifier aNetifId
#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 otPlatUdpConnect(otUdpSocket *aUdpSocket)
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 @@ otError otPlatUdpConnect(otUdpSocket *aUdpSocket)
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 @@ otError otPlatUdpConnect(otUdpSocket *aUdpSocket)
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 @@ otError otPlatUdpConnect(otUdpSocket *aUdpSocket)
{
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 @@ otError otPlatUdpConnect(otUdpSocket *aUdpSocket)
#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 @@ otError otPlatUdpJoinMulticastGroup(otUdpSocket *aUdpSocket,
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 @@ otError otPlatUdpJoinMulticastGroup(otUdpSocket *aUdpSocket,
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 @@ otError otPlatUdpLeaveMulticastGroup(otUdpSocket *aUdpSocket,
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 @@ otError otPlatUdpLeaveMulticastGroup(otUdpSocket *aUdpSocket,
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 adc252ac4..f22a8cf22 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 4ee7a1ae8..449ebe5fd 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 @@ public:
} // 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 000000000..00ce08710
--- /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 000000000..e8c820135
--- /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 000000000..2eaff4f36
--- /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 5c66bf973..2ae828b55 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 @@ class SingleHostAndService(thread_cert.TestCase):
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 @@ class SingleHostAndService(thread_cert.TestCase):
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 536d3adca..78edac833 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 @@ class ManualMulticastAddressConfig(thread_cert.TestCase):
# 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 5359848e8..9f5d337f9 100755
--- a/tests/scripts/thread-cert/node.py
+++ b/tests/scripts/thread-cert/node.py
@@ -197,6 +197,9 @@ class OtbrDocker:
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 @@ class NodeImpl:
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 @@ class NodeImpl:
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 @@ class NodeImpl:
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 @@ class NodeImpl:
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 @@ class NodeImpl:
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 @@ class NodeImpl:
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 954df9844..c32f8b3fb 100755
--- a/tests/scripts/thread-cert/test_detach.py
+++ b/tests/scripts/thread-cert/test_detach.py
@@ -105,7 +105,7 @@ class TestDetach(thread_cert.TestCase):
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 @@ class TestDetach(thread_cert.TestCase):
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 000000000..595ffea18
--- /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 d40055744..246343421 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 @@ class MleMsgKeySeqJump(thread_cert.TestCase):
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 @@ class MleMsgKeySeqJump(thread_cert.TestCase):
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 7192e6f43..c31ac6e52 100755
--- a/tests/scripts/thread-cert/test_netdata_publisher.py
+++ b/tests/scripts/thread-cert/test_netdata_publisher.py
@@ -70,7 +70,7 @@ DNSSRP_PORT = 49152
# 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,70 @@ class NetDataPublisher(thread_cert.TestCase):
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)
+ 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.netdata_publish_dnssrp_unicast(DNSSRP_ADDRESS, DNSSRP_PORT)
+ 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 entries
+
+ 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.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 vs anycast
num = 0
for node in routers:
@@ -316,20 +334,46 @@ class NetDataPublisher(thread_cert.TestCase):
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)
+ # 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_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_UNCIAST))
+ self.assertEqual(len(services), min(num, DESIRED_NUM_DNSSRP_UNICAST))
self.verify_unicast_services(services)
for node in routers:
@@ -494,15 +538,15 @@ class NetDataPublisher(thread_cert.TestCase):
# 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 d78dc118b..b38bcfe9e 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 @@ class SrpServerRebootPort(thread_cert.TestCase):
#
# 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 6f8b01c02..146b2b55b 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 @@ class Router_5_1_01(thread_cert.TestCase):
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 @@ class Router_5_1_01(thread_cert.TestCase):
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 @@ class Router_5_1_01(thread_cert.TestCase):
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 36d52640e..888a6d9ad 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 @@ class TestBackboneRouterService(thread_cert.TestCase):
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 @@ class TestBackboneRouterService(thread_cert.TestCase):
# 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 @@ class TestBackboneRouterService(thread_cert.TestCase):
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 @@ class TestBackboneRouterService(thread_cert.TestCase):
# 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 7a98680a2..81c9054fb 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 @@ class TestDomainUnicastAddress(thread_cert.TestCase):
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 @@ class TestDomainUnicastAddress(thread_cert.TestCase):
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 @@ class TestDomainUnicastAddress(thread_cert.TestCase):
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 4053feca0..76772abe9 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 @@ class TestDomainUnicastAddressRegistration(thread_cert.TestCase):
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 18875362c..ac974de5b 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 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
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 @@ class TestMulticastListenerRegistration(thread_cert.TestCase):
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 7b55b91d3..caf979f19 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 @@ class TestParentSelection(thread_cert.TestCase):
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 @@ class TestParentSelection(thread_cert.TestCase):
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 @@ class TestParentSelection(thread_cert.TestCase):
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 @@ class TestParentSelection(thread_cert.TestCase):
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 @@ class TestParentSelection(thread_cert.TestCase):
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 0c9a6983c..4b91cd431 100755
--- a/tests/toranj/build.sh
+++ b/tests/toranj/build.sh
@@ -113,8 +113,9 @@ case ${build_config} in
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 @@ case ${build_config} in
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 @@ case ${build_config} in
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 @@ case ${build_config} in
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 @@ case ${build_config} in
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 @@ case ${build_config} in
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 @@ case ${build_config} in
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 @@ case ${build_config} in
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 @@ case ${build_config} in
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 @@ case ${build_config} in
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 @@ case ${build_config} in
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 @@ case ${build_config} in
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 @@ case ${build_config} in
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 @@ case ${build_config} in
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 385d8e584..8f278f437 100644
--- a/tests/toranj/cli/cli.py
+++ b/tests/toranj/cli/cli.py
@@ -223,6 +223,17 @@ class Node(object):
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 @@ class Node(object):
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# 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 @@ class Node(object):
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 @@ class Node(object):
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 @@ class Node(object):
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 @@ class Node(object):
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 @@ def verify_within(condition_checker_func, wait_time, arg=None, delay_time=0.1):
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 a3a4086a7..bf524878f 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 @@ nodes = [r1, r2, c2]
# -----------------------------------------------------------------------------------------------------------------------
# Test Implementation
-common_prefix = 'fd00:cafe::'
-prefix1 = 'fd00:1::'
-prefix2 = 'fd00:2::'
-prefix3 = 'fd00:3::'
-
# Each node adds its own prefix.
r1.add_prefix('fd00:1::/64', 'paros', 'med')
r2.add_prefix('fd00:2::/64', 'paros', 'med')
@@ -104,6 +99,9 @@ r1.add_prefix('fd00:abba::/64', 'paros', 'high')
r2.add_prefix('fd00:abba::/64', 'paros', 'med')
c2.add_prefix('fd00:abba::/64', 'paros', 'low')
+r1.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 @@ c2.register_netdata()
def check_netdata_on_all_nodes():
for node in nodes:
netdata = node.get_netdata()
- prefixes = netdata['prefixes']
- verify(len(prefixes) == 6)
+ verify(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 @@ r2.interface_down()
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 a8bb7daaa..463854a9c 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 @@ VENDOR_APP_URL = 35
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 000000000..47a02ee01
--- /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 e65d6fffb..0e3edb2bc 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['weight'] == '1')
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 000000000..2462d0a05
--- /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 000000000..12c61a7e7
--- /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 000000000..ecf41b999
--- /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 000000000..71d2ebc1b
--- /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 0ddf3584b..596cfdc57 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 @@ def check_channel():
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 @@ 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)
# Set channels 12-15 as favorable and request a channel select, verify
# that channel is switched to 12.
@@ -112,13 +116,13 @@ node.cli('channel manager favored', chan_12_to_15_mask)
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 @@ verify_within(check_channel, 2)
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 @@ 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)
# Set channels 12-15 as favorable and request a channel select, verify
# that channel is not switched.
@@ -145,10 +149,11 @@ verify_within(check_channel, 2)
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 @@ verify_within(check_channel, 2)
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 60e86777f..2fced0032 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 bc933d17f..f564e0bc9 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 f2bcbb80c..c1a968916 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 5915459af..04cef966c 100755
--- a/tests/toranj/start.sh
+++ b/tests/toranj/start.sh
@@ -192,7 +192,12 @@ if [ "$TORANJ_CLI" = 1 ]; then
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 1a9b18228..92337b30c 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -1180,6 +1180,27 @@ target_link_libraries(ot-test-string
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 286870908..4740a2fff 100644
--- a/tests/unit/test_network_data.cpp
+++ b/tests/unit/test_network_data.cpp
@@ -86,32 +86,32 @@ bool CompareOnMeshPrefixConfig(const otBorderRouterConfig &aConfig1, const otBor
(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 @@ void TestNetworkDataIterator(void)
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 @@ void TestNetworkDataIterator(void)
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 @@ void TestNetworkDataIterator(void)
},
};
- 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 @@ void TestNetworkDataIterator(void)
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 @@ void TestNetworkDataDsnSrpServices(void)
{"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 40e23ceb4..2904f6019 100644
--- a/tests/unit/test_platform.cpp
+++ b/tests/unit/test_platform.cpp
@@ -694,39 +694,39 @@ OT_TOOL_WEAK otError otPlatSetMcuPowerState(otInstance *aInstance, otPlatMcuPowe
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 @@ otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, con
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 25156378c..b1a3877ff 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";
-
- 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 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[] = "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";
-
- 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 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";
+
+ 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 @@ void TestExampleInSpec(void)
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 df7c2b225..a474de4b1 100644
--- a/tests/unit/test_routing_manager.cpp
+++ b/tests/unit/test_routing_manager.cpp
@@ -126,7 +126,7 @@ static otRadioFrame sRadioTxFrame;
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 @@ bool sRespondToNs; // Indicates whether or not to respond to NS.
ExpectedPio sExpectedPio; // Expected PIO in the emitted RA by BR (MUST be seen in RA to set `sRaValidated`).
uint32_t sOnLinkLifetime; // Valid lifetime for local on-link prefix from the last processed RA.
+// 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 @@ void ValidateRouterAdvert(const Icmp6Packet &aPacket)
{
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 @@ void ValidateRouterAdvert(const Icmp6Packet &aPacket)
VerifyOrQuit(raMsg.IsValid());
- VerifyOrQuit(raMsg.GetHeader().GetRouterLifetime() == 0);
+ if (sCheckRaHeaderLifetime)
+ {
+ VerifyOrQuit(raMsg.GetHeader().GetRouterLifetime() == sExpectedRaHeaderLifetime);
+ }
switch (sExpectedRaHeaderFlags)
{
@@ -570,7 +579,7 @@ exit:
void LogRouterAdvert(const Icmp6Packet &aPacket)
{
- Ip6::Nd::RouterAdvertMessage raMsg(aPacket);
+ Ip6::Nd::RouterAdvert::RxMessage raMsg(aPacket);
VerifyOrQuit(raMsg.IsValid());
@@ -854,17 +863,15 @@ struct RaFlags : public Clearable<RaFlags>
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 @@ uint16_t BuildRouterAdvert(uint8_t (&aBuffer)[N],
header.SetOtherConfigFlag();
}
- {
- 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));
- }
+ SuccessOrQuit(aRaMsg.AppendHeader(header));
- for (; aNumRios > 0; aRios++, aNumRios--)
- {
- SuccessOrQuit(raMsg.AppendRouteInfoOption(aRios->mPrefix, aRios->mValidLifetime, aRios->mPreference));
- }
+ if (aRaFlags.mStubRouterFlag)
+ {
+ SuccessOrQuit(aRaMsg.AppendFlagsExtensionOption(/* aStubRouterFlag */ true));
+ }
- length = raMsg.GetAsPacket().GetLength();
+ for (; aNumPios > 0; aPios++, aNumPios--)
+ {
+ SuccessOrQuit(aRaMsg.AppendPrefixInfoOption(aPios->mPrefix, aPios->mValidLifetime, aPios->mPreferredLifetime));
}
- return length;
+ for (; aNumRios > 0; aRios++, aNumRios--)
+ {
+ SuccessOrQuit(aRaMsg.AppendRouteInfoOption(aRios->mPrefix, aRios->mValidLifetime, aRios->mPreference));
+ }
}
void SendRouterAdvert(const Ip6::Address &aRouterAddress,
@@ -912,12 +912,15 @@ void SendRouterAdvert(const Ip6::Address &aRouterAddress,
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;
+
+ BuildRouterAdvert(raMsg, aPios, aNumPios, aRios, aNumRios, aDefaultRoute, aRaFlags);
+ raMsg.GetAsPacket(packet);
- SendRouterAdvert(aRouterAddress, buffer, length);
+ 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 @@ void SendRouterAdvert(const Ip6::Address &aRouterAddress, const RaFlags &aRaFlag
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 @@ void InitTest(bool aEnablBorderRouting = false, bool aAfterReset = false)
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 @@ void TestLearningAndCopyingOfFlags(void)
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 @@ int main(void)
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 000000000..6767a0f49
--- /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 a3e06ac63..29aa49e7a 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
index fa2ebbb24..92539c12f 100644..100755
--- a/tools/tcat_ble_client/bbtc.py
+++ b/tools/tcat_ble_client/bbtc.py
@@ -34,6 +34,7 @@ import logging
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 @@ async def main():
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 @@ async def main():
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 @@ async def get_device_by_args(args):
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 9d15b7945..4731c18e2 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 asyncio
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 @@ class BleStreamSecure:
# 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 @@ class BleStreamSecure:
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 @@ class BleStreamSecure:
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 @@ class BleStreamSecure:
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 000000000..542194046
--- /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