Merge remote-tracking branch 'github/main' into HEAD

<fxrev.dev/678949> needs to land prior to this CL.

Upstream-Commit: 645fbcd400d2e6c2de641bccb0335e3eaaed1f36
Change-Id: I9e2c785bcae75f25d5ad57d30330bcf3132d0344
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 160bc34..479ca7f 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,3 +1,3 @@
 GitHub Issues are for bugs and feature requests. To make bugs and feature requests more easy to find and organize, we close issues that are deemed out of scope for GitHub Issues.
 
-The openthread-users Google Group is the recommended place for users to discuss OpenThread and interact directly with the OpenThread community. https://groups.google.com/forum/#!forum/openthread-users
+OpenThread GitHub Discussions is the recommended place for users to discuss OpenThread and interact directly with the OpenThread community. https://github.com/openthread/openthread/discussions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ac4e986..fe08b5f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -84,6 +84,7 @@
     name: package-${{ matrix.compiler }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         include:
           - compiler: gcc
@@ -134,7 +135,7 @@
     - uses: actions/checkout@v2
       with:
         repository: ARMmbed/mbedtls
-        ref: v3.0.0
+        ref: v3.1.0
         path: third_party/mbedtls/repo
     - name: Build
       run: |
@@ -144,6 +145,7 @@
     name: arm-gcc-${{ matrix.gcc_ver }}
     runs-on: ubuntu-18.04
     strategy:
+      fail-fast: false
       matrix:
         include:
           - gcc_ver: 4
@@ -184,8 +186,9 @@
     name: gcc-${{ matrix.gcc_ver }}
     runs-on: ubuntu-18.04
     strategy:
+      fail-fast: false
       matrix:
-        gcc_ver: [5, 6, 7, 8, 9, 10]
+        gcc_ver: [5, 6, 7, 8, 9, 10, 11]
     env:
       CC: gcc-${{ matrix.gcc_ver }}
       CXX: g++-${{ matrix.gcc_ver }}
@@ -196,6 +199,11 @@
     - name: Bootstrap
       run: |
         sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
+        case ${{ matrix.gcc_ver }} in
+          11)
+            sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
+            ;;
+        esac
         sudo apt-get --no-install-recommends install -y gcc-${{ matrix.gcc_ver }} g++-${{ matrix.gcc_ver }} ninja-build libreadline-dev libncurses-dev
     - name: Build
       run: |
@@ -206,6 +214,7 @@
     name: clang-${{ matrix.clang_ver }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         clang_ver: ["6.0", "7", "8", "9"]
     env:
@@ -228,6 +237,7 @@
     name: clang-m32-${{ matrix.clang_ver }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         clang_ver: ["6.0", "7", "8", "9"]
     env:
@@ -273,6 +283,7 @@
   macos:
     name: macos-${{ matrix.CC }}
     strategy:
+      fail-fast: false
       matrix:
         include:
           - CC: clang
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 627f0a9..339bb1a 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -44,10 +44,10 @@
     name: buildx-${{ matrix.docker_name }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         include:
           - docker_name: environment
-          - docker_name: codelab_otsim
     steps:
     - uses: actions/checkout@v2
       with:
diff --git a/.github/workflows/otbr.yml b/.github/workflows/otbr.yml
index 173035f..e1c87b8 100644
--- a/.github/workflows/otbr.yml
+++ b/.github/workflows/otbr.yml
@@ -60,53 +60,33 @@
     - uses: actions/checkout@v2
       with:
         submodules: true
-    - name: Get Border Router Test ID
-      id: unique_action_id
-      env:
-        GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
-      run: |
-        ./script/git-tool clone https://github.com/openthread/ot-br-posix.git --depth 1 "/tmp/otbr_unique_action_id"
-        echo ::set-output  name=id::${GITHUB_WORKFLOW}-${GITHUB_JOB}-${GITHUB_RUN_ID}-$(cd /tmp/otbr_unique_action_id/ && git rev-parse HEAD)
-    - name: Check cached result
-      id: check_cache_result
-      uses: actions/cache@v2
-      with:
-        path: |
-          _test_complete_
-        key: "_test_complete_${{ steps.unique_action_id.outputs.id }}"
     - name: Build OTBR Docker
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       env:
         GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
       run: |
         ./script/test build_otbr_docker
     - name: Bootstrap
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
         sudo apt-get --no-install-recommends install -y python3-setuptools python3-wheel ninja-build socat lcov
         python3 -m pip install -r tests/scripts/thread-cert/requirements.txt
     - name: Build
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         ./script/test build
     - name: Get Thread-Wireshark
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         ./script/test get_thread_wireshark
     - name: Run
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         export CI_ENV="$(bash <(curl -s https://codecov.io/env)) -e GITHUB_ACTIONS -e COVERAGE"
         echo "CI_ENV=${CI_ENV}"
         sudo -E ./script/test cert_suite ./tests/scripts/thread-cert/backbone/*.py || (sudo chmod a+r *.log *.json *.pcap && false)
     - uses: actions/upload-artifact@v2
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       with:
         name: cov-thread-1-2-backbone-docker
         path: /tmp/coverage/
     - uses: actions/upload-artifact@v2
-      if: ${{ failure() && steps.check_cache_result.outputs.cache-hit != 'true' }}
+      if: ${{ failure() }}
       with:
         name: thread-1-2-backbone-results
         path: |
@@ -116,18 +96,12 @@
           coredump_*
           otbr-agent_*
     - name: Generate Coverage
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         ./script/test generate_coverage gcc
     - uses: actions/upload-artifact@v2
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       with:
         name: cov-thread-1-2-backbone
         path: tmp/coverage.info
-    - name: Cache test result
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
-      run: |
-        mkdir _test_complete_
 
   thread-border-router:
     runs-on: ubuntu-20.04
@@ -176,54 +150,34 @@
       MAX_JOBS: 3
     steps:
     - uses: actions/checkout@v2
-    - name: Get Border Router Test ID
-      id: unique_action_id
-      env:
-        GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
-      run: |
-        ./script/git-tool clone https://github.com/openthread/ot-br-posix.git --depth 1 "/tmp/otbr_unique_action_id"
-        echo ::set-output  name=id::${GITHUB_WORKFLOW}-${GITHUB_JOB}-${GITHUB_RUN_ID}-$(cd /tmp/otbr_unique_action_id/ && git rev-parse HEAD)-${{matrix.otbr_mdns}}-trel${{matrix.otbr_trel}}
-    - name: Check cached result
-      id: check_cache_result
-      uses: actions/cache@v2
-      with:
-        path: |
-          _test_complete_
-        key: "_test_complete_${{ steps.unique_action_id.outputs.id }}"
     - name: Build OTBR Docker
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       env:
         GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
         TREL: ${{ matrix.otbr_trel }}
       run: |
         ./script/test build_otbr_docker
     - name: Bootstrap
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
         sudo apt-get --no-install-recommends install -y python3-setuptools python3-wheel ninja-build socat lcov
         python3 -m pip install -r tests/scripts/thread-cert/requirements.txt
     - name: Build
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         ./script/test build
     - name: Get Thread-Wireshark
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         ./script/test get_thread_wireshark
     - name: Run
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         export CI_ENV="$(bash <(curl -s https://codecov.io/env)) -e GITHUB_ACTIONS -e COVERAGE"
         echo "CI_ENV=${CI_ENV}"
         sudo -E ./script/test cert_suite ${{ matrix.cert_scripts }} || (sudo chmod a+r *.log *.json *.pcap && false)
     - uses: actions/upload-artifact@v2
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       with:
         name: cov-thread-border-router-docker
         path: /tmp/coverage/
     - uses: actions/upload-artifact@v2
-      if: ${{ failure() && steps.check_cache_result.outputs.cache-hit != 'true' }}
+      if: ${{ failure() }}
       with:
         name: thread-border-router-results
         path: |
@@ -233,18 +187,12 @@
           coredump_*
           otbr-agent_*
     - name: Generate Coverage
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       run: |
         ./script/test generate_coverage gcc
     - uses: actions/upload-artifact@v2
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
       with:
         name: cov-thread-border-router
         path: tmp/coverage.info
-    - name: Cache test result
-      if: ${{ success() && steps.check_cache_result.outputs.cache-hit != 'true' }}
-      run: |
-        mkdir _test_complete_
 
   upload-coverage:
     needs:
@@ -274,7 +222,6 @@
 
   delete-coverage-artifacts:
     needs: upload-coverage
-    if: always()
     runs-on: ubuntu-20.04
     steps:
     - uses: geekyeggo/delete-artifact@1-glob-support
diff --git a/.github/workflows/otci.yml b/.github/workflows/otci.yml
index 0d7acf6..5023d7c 100644
--- a/.github/workflows/otci.yml
+++ b/.github/workflows/otci.yml
@@ -44,6 +44,7 @@
     name: cli-sim VIRTUAL_TIME=${{ matrix.virtual_time }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         virtual_time: [0, 1]
     env:
diff --git a/.github/workflows/otns.yml b/.github/workflows/otns.yml
index 2d37956..46dc33c 100644
--- a/.github/workflows/otns.yml
+++ b/.github/workflows/otns.yml
@@ -132,6 +132,7 @@
     name: Stress ${{ matrix.suite }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         include:
           - suite: "network-forming"
diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml
index fc57a19..abe6f0b 100644
--- a/.github/workflows/posix.yml
+++ b/.github/workflows/posix.yml
@@ -183,6 +183,7 @@
     name: pty-linux OT_DAEMON=${{ matrix.OT_DAEMON }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         OT_DAEMON: ['off', 'on']
     env:
@@ -227,6 +228,7 @@
     name: pty-macos OT_DAEMON=${{ matrix.OT_DAEMON }}
     runs-on: macos-10.15
     strategy:
+      fail-fast: false
       matrix:
         OT_DAEMON: ['off', 'on']
     env:
@@ -298,7 +300,6 @@
 
   delete-coverage-artifacts:
     needs: upload-coverage
-    if: always()
     runs-on: ubuntu-20.04
     steps:
     - uses: geekyeggo/delete-artifact@1-glob-support
diff --git a/.github/workflows/simulation-1.1.yml b/.github/workflows/simulation-1.1.yml
index 489759a..a16bf12 100644
--- a/.github/workflows/simulation-1.1.yml
+++ b/.github/workflows/simulation-1.1.yml
@@ -150,6 +150,7 @@
     name: cli-mtd MESSAGE_USE_HEAP=${{ matrix.message_use_heap }}
     runs-on: ubuntu-20.04
     strategy:
+      fail-fast: false
       matrix:
         message_use_heap: [0, 1]
     env:
@@ -347,97 +348,6 @@
         name: cov-multiple-instance
         path: tmp/coverage.info
 
-  ncp-gcc-m32:
-    runs-on: ubuntu-20.04
-    env:
-      CFLAGS: -m32
-      CXXFLAGS: -m32
-      LDFLAGS: -m32
-      COVERAGE: 1
-      GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
-      NODE_TYPE: ncp-sim
-      PYTHONUNBUFFERED: 1
-      REFERENCE_DEVICE: 1
-      THREAD_VERSION: 1.1
-      VIRTUAL_TIME: 1
-    steps:
-    - uses: actions/checkout@v2
-      with:
-        submodules: true
-    - name: Bootstrap
-      run: |
-        sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
-        sudo apt-get --no-install-recommends install -y g++-multilib python3-setuptools python3-wheel lcov
-        python3 -m pip install -r tests/scripts/thread-cert/requirements.txt
-        pyspineldir=$(mktemp -d -t pyspinel_XXXXXX)
-        ./script/git-tool clone https://github.com/openthread/pyspinel.git --depth 1 "${pyspineldir}"
-        cd "${pyspineldir}"
-        pip3 install .
-    - name: Build
-      run: |
-        ./bootstrap
-        make -f examples/Makefile-simulation
-    - name: Run
-      run: |
-        VERBOSE=1 make -f examples/Makefile-simulation check
-    - uses: actions/upload-artifact@v2
-      if: ${{ failure() }}
-      with:
-        name: ncp-gcc-m32-thread-cert
-        path: build/simulation/tests/scripts/thread-cert
-    - name: Generate Coverage
-      run: |
-        ./script/test generate_coverage gcc
-    - uses: actions/upload-artifact@v2
-      with:
-        name: cov-ncp-gcc-m32
-        path: tmp/coverage.info
-
-  ncp-clang:
-    runs-on: ubuntu-20.04
-    env:
-      COVERAGE: 1
-      GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
-      NODE_TYPE: ncp-sim
-      PYTHONUNBUFFERED: 1
-      REFERENCE_DEVICE: 1
-      THREAD_VERSION: 1.1
-      VIRTUAL_TIME: 1
-      CC: clang-10
-      CXX: clang++-10
-    steps:
-    - uses: actions/checkout@v2
-      with:
-        submodules: true
-    - name: Bootstrap
-      run: |
-        sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
-        sudo apt-get --no-install-recommends install -y clang-10 clang++-10 python3-setuptools python3-wheel llvm lcov
-        python3 -m pip install -r tests/scripts/thread-cert/requirements.txt
-        pyspineldir=$(mktemp -d -t pyspinel_XXXXXX)
-        ./script/git-tool clone https://github.com/openthread/pyspinel.git --depth 1 "${pyspineldir}"
-        cd "${pyspineldir}"
-        pip3 install .
-    - name: Build
-      run: |
-        ./bootstrap
-        make -f examples/Makefile-simulation
-    - name: Run
-      run: |
-        VERBOSE=1 make -f examples/Makefile-simulation check
-    - uses: actions/upload-artifact@v2
-      if: ${{ failure() }}
-      with:
-        name: cli-clang-thread-cert
-        path: build/simulation/tests/scripts/thread-cert
-    - name: Generate Coverage
-      run: |
-        ./script/test generate_coverage llvm
-    - uses: actions/upload-artifact@v2
-      with:
-        name: cov-ncp-clang
-        path: tmp/coverage.info
-
   upload-coverage:
     needs:
     - packet-verification
@@ -447,8 +357,6 @@
     - expects
     - ot-commissioner
     - multiple-instance
-    - ncp-gcc-m32
-    - ncp-clang
     runs-on: ubuntu-20.04
     steps:
     - uses: actions/checkout@v2
@@ -471,7 +379,6 @@
 
   delete-coverage-artifacts:
     needs: upload-coverage
-    if: always()
     runs-on: ubuntu-20.04
     steps:
     - uses: geekyeggo/delete-artifact@1-glob-support
diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.2.yml
index df19047..f8bc267 100644
--- a/.github/workflows/simulation-1.2.yml
+++ b/.github/workflows/simulation-1.2.yml
@@ -54,6 +54,7 @@
       CC: ${{ matrix.compiler.c }}
       CXX: ${{ matrix.compiler.cxx }}
     strategy:
+      fail-fast: false
       matrix:
         compiler: [{c: "gcc", cxx: "g++", gcov: "gcc"}, { c: "clang-10", cxx: "clang++-10", gcov: "llvm"}]
         arch: ["m32", "m64"]
@@ -329,7 +330,6 @@
 
   delete-coverage-artifacts:
     needs: upload-coverage
-    if: always()
     runs-on: ubuntu-20.04
     steps:
     - uses: geekyeggo/delete-artifact@1-glob-support
diff --git a/.github/workflows/toranj.yml b/.github/workflows/toranj.yml
index ff88daa..46f9520 100644
--- a/.github/workflows/toranj.yml
+++ b/.github/workflows/toranj.yml
@@ -40,10 +40,11 @@
         GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
       if: "github.ref != 'refs/heads/main'"
 
-  toranj:
-    name: toranj-${{ matrix.TORANJ_RADIO }}
+  toranj-ncp:
+    name: toranj-ncp-${{ matrix.TORANJ_RADIO }}
     runs-on: ubuntu-18.04
     strategy:
+      fail-fast: false
       matrix:
         TORANJ_RADIO: ['15.4', 'trel', 'multi']
     env:
@@ -82,12 +83,47 @@
     - uses: actions/upload-artifact@v2
       if: "matrix.TORANJ_RADIO != 'multi'"
       with:
-        name: cov-toranj-${{ matrix.TORANJ_RADIO }}
+        name: cov-toranj-ncp-${{ matrix.TORANJ_RADIO }}
+        path: tmp/coverage.info
+
+  toranj-cli:
+    name: toranj-cli-${{ matrix.TORANJ_RADIO }}
+    runs-on: ubuntu-18.04
+    strategy:
+      matrix:
+        TORANJ_RADIO: ['15.4']
+    env:
+      COVERAGE: 1
+      TORANJ_RADIO : ${{ matrix.TORANJ_RADIO }}
+      TORANJ_CLI: 1
+    steps:
+    - uses: actions/checkout@v2
+      with:
+        submodules: true
+    - name: Bootstrap
+      env:
+        GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+      run: |
+        sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
+        sudo apt-get --no-install-recommends install -y lcov
+        python3 -m pip install -r tests/scripts/thread-cert/requirements.txt
+    - name: Build & Run
+      run: |
+        top_builddir=$(pwd)/build/toranj ./tests/toranj/start.sh
+    - name: Generate Coverage
+      if: "matrix.TORANJ_RADIO != 'multi'"
+      run: |
+        ./script/test generate_coverage gcc
+    - uses: actions/upload-artifact@v2
+      if: "matrix.TORANJ_RADIO != 'multi'"
+      with:
+        name: cov-toranj-cli-${{ matrix.TORANJ_RADIO }}
         path: tmp/coverage.info
 
   upload-coverage:
     needs:
-    - toranj
+    - toranj-ncp
+    - toranj-cli
     runs-on: ubuntu-18.04
     steps:
     - uses: actions/checkout@v2
@@ -110,7 +146,6 @@
 
   delete-coverage-artifacts:
     needs: upload-coverage
-    if: always()
     runs-on: ubuntu-20.04
     steps:
     - uses: geekyeggo/delete-artifact@1-glob-support
diff --git a/Android.mk b/Android.mk
index dba1524..4d09fcc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -35,17 +35,24 @@
 
 OPENTHREAD_PROJECT_CFLAGS                                                 ?= \
     -DOPENTHREAD_PROJECT_CORE_CONFIG_FILE=\"openthread-core-posix-config.h\" \
-    -DOPENTHREAD_CONFIG_FILE=\<openthread-config-android.h\>                 \
     $(NULL)
 
 OPENTHREAD_PUBLIC_CFLAGS                                         := \
-    -DOPENTHREAD_CONFIG_PING_SENDER_ENABLE=1                        \
-    -DOPENTHREAD_CONFIG_COMMISSIONER_ENABLE=1                       \
+    -DOPENTHREAD_CONFIG_BORDER_AGENT_ENABLE=1                       \
+    -DOPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE=1                      \
+    -DOPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE=1                  \
+    -DOPENTHREAD_CONFIG_DTLS_ENABLE=1                               \
     -DOPENTHREAD_CONFIG_IP6_SLAAC_ENABLE=1                          \
+    -DOPENTHREAD_CONFIG_JAM_DETECTION_ENABLE=1                      \
+    -DOPENTHREAD_CONFIG_JOINER_ENABLE=1                             \
     -DOPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE=1                  \
     -DOPENTHREAD_CONFIG_MAC_FILTER_ENABLE=1                         \
-    -DOPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE=1                      \
+    -DOPENTHREAD_CONFIG_NCP_HDLC_ENABLE=1                           \
+    -DOPENTHREAD_CONFIG_PING_SENDER_ENABLE=1                        \
+    -DOPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE=1                \
     -DOPENTHREAD_FTD=1                                              \
+    -DOPENTHREAD_PLATFORM_POSIX=1                                   \
+    -DOPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE=1                      \
     -DOPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE=1          \
     $(NULL)
 
@@ -228,10 +235,10 @@
     src/core/common/heap_data.cpp                                   \
     src/core/common/heap_string.cpp                                 \
     src/core/common/instance.cpp                                    \
-    src/core/common/logging.cpp                                     \
+    src/core/common/log.cpp                                         \
     src/core/common/message.cpp                                     \
     src/core/common/notifier.cpp                                    \
-    src/core/common/random_manager.cpp                              \
+    src/core/common/random.cpp                                      \
     src/core/common/settings.cpp                                    \
     src/core/common/string.cpp                                      \
     src/core/common/tasklet.cpp                                     \
@@ -244,6 +251,7 @@
     src/core/crypto/aes_ecb.cpp                                     \
     src/core/crypto/crypto_platform.cpp                             \
     src/core/crypto/ecdsa.cpp                                       \
+    src/core/crypto/ecdsa_tinycrypt.cpp                             \
     src/core/crypto/hkdf_sha256.cpp                                 \
     src/core/crypto/hmac_sha256.cpp                                 \
     src/core/crypto/mbedtls.cpp                                     \
@@ -272,11 +280,13 @@
     src/core/meshcop/dataset_updater.cpp                            \
     src/core/meshcop/dtls.cpp                                       \
     src/core/meshcop/energy_scan_client.cpp                         \
+    src/core/meshcop/extended_panid.cpp                             \
     src/core/meshcop/joiner.cpp                                     \
     src/core/meshcop/joiner_router.cpp                              \
     src/core/meshcop/meshcop.cpp                                    \
     src/core/meshcop/meshcop_leader.cpp                             \
     src/core/meshcop/meshcop_tlvs.cpp                               \
+    src/core/meshcop/network_name.cpp                               \
     src/core/meshcop/panid_query_client.cpp                         \
     src/core/meshcop/timestamp.cpp                                  \
     src/core/net/checksum.cpp                                       \
@@ -404,6 +414,7 @@
     third_party/mbedtls/repo/library/cipher.c                       \
     third_party/mbedtls/repo/library/cipher_wrap.c                  \
     third_party/mbedtls/repo/library/cmac.c                         \
+    third_party/mbedtls/repo/library/constant_time.c                \
     third_party/mbedtls/repo/library/ctr_drbg.c                     \
     third_party/mbedtls/repo/library/debug.c                        \
     third_party/mbedtls/repo/library/des.c                          \
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 540550e..f311fc9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -58,7 +58,6 @@
     target_compile_definitions(ot-config INTERFACE "BYTE_ORDER_BIG_ENDIAN=1")
 endif()
 
-include("${PROJECT_SOURCE_DIR}/etc/cmake/checks.cmake")
 include("${PROJECT_SOURCE_DIR}/etc/cmake/options.cmake")
 include("${PROJECT_SOURCE_DIR}/etc/cmake/functions.cmake")
 
diff --git a/NOTICE b/NOTICE
index 962490e..94a2d71 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,5 +1,5 @@
-OpenThread is an open source implementation of the Thread 1.1.1 Final Specification.
-The Thread 1.1.1 Final Specification is promulgated by the Thread Group. The Thread
+OpenThread is an open source implementation of the Thread 1.2.0 Final Specification.
+The Thread 1.2.0 Final Specification is promulgated by the Thread Group. The Thread
 Group is a non-profit organization formed for the purposes of defining one or
 more specifications, best practices, reference architectures, implementation
 guidelines and certification programs to promote the availability of compliant
@@ -7,10 +7,10 @@
 information about the benefits thereof, can be found at http://threadgroup.org.
 
 OpenThread is not affiliated with or endorsed by the Thread Group. Implementation
-of this OpenThread code does not assure compliance with the Thread 1.1.1 Final
+of this OpenThread code does not assure compliance with the Thread 1.2.0 Final
 Specification and does not convey the right to identify any final product as Thread
 certified. Members of the Thread Group may hold patents and other intellectual
-property rights relating to the Thread 1.1.1 Final Specification, ownership and
+property rights relating to the Thread 1.2.0 Final Specification, ownership and
 licenses of which are subject to the Thread Group’s IP Policies, and not this license.
 
 The included copyright to the OpenThread code is subject to the license in the
diff --git a/README.md b/README.md
index 5370128..6574588 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
 
 **...OS and platform agnostic**, with a narrow platform abstraction layer and a small memory footprint, making it highly portable. It supports both system-on-chip (SoC) and network co-processor (NCP) designs.
 
-**...a Thread Certified Component**, implementing all features defined in the [Thread 1.1.1 specification](https://www.threadgroup.org/support#specifications), including all Thread networking layers (IPv6, 6LoWPAN, IEEE 802.15.4 with MAC security, Mesh Link Establishment, Mesh Routing) and device roles, as well as [Border Router](https://github.com/openthread/ot-br-posix) support.
+**...a Thread Certified Component**, implementing all features defined in the [Thread 1.2 specification](https://www.threadgroup.org/support#specifications), including all Thread networking layers (IPv6, 6LoWPAN, IEEE 802.15.4 with MAC security, Mesh Link Establishment, Mesh Routing) and device roles, as well as [Border Router](https://github.com/openthread/ot-br-posix) support.
 
 More information about Thread can be found at [threadgroup.org](http://threadgroup.org/). Thread is a registered trademark of the Thread Group, Inc.
 
@@ -29,7 +29,7 @@
 
 # Who supports OpenThread?
 
-<a href="https://www.arm.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-arm.png" alt="ARM" width="200px"></a><a href="https://www.cascoda.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-cascoda.png" alt="Cascoda" width="200px"></a><a href="https://www.espressif.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-espressif-github.png" alt="Espressif" width="200px"></a><a href="https://www.google.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-google.png" alt="Google" width="200px"></a><a href="http://www.nordicsemi.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-nordic.png" alt="Nordic" width="200px"></a><a href="http://www.nxp.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-nxp.png" alt="NXP" width="200px"></a><a href="http://www.qorvo.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-qorvo.png" alt="Qorvo" width="200px"></a><a href="https://www.qualcomm.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-qc.png" alt="Qualcomm" width="200px"></a><a href="https://www.samsung.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-samsung.png" alt="Samsung" width="200px"></a><a href="https://www.silabs.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-silabs.png" alt="Silicon Labs" width="200px"></a><a href="https://www.st.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-stm.png" alt="STMicroelectronics" width="200px"></a><a href="https://www.synopsys.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-synopsys.png" alt="Synopsys" width="200px"></a><a href="https://www.telink-semi.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-telink-github.png" alt="Telink Semiconductor" width="200px"></a><a href="https://www.ti.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-ti.png" alt="Texas Instruments" width="200px"></a><a href="https://www.zephyrproject.org/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-zephyr.png" alt="Zephyr Project" width="200px"></a>
+<a href="https://www.arm.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-arm.png" alt="ARM" width="200px"></a><a href="https://www.cascoda.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-cascoda.png" alt="Cascoda" width="200px"></a><a href="https://www.espressif.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-espressif-github.png" alt="Espressif" width="200px"></a><a href="https://www.google.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-google.png" alt="Google" width="200px"></a><a href="https://www.infineon.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-infineon.png" alt="Infineon" width="200px"></a><a href="http://www.nordicsemi.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-nordic.png" alt="Nordic" width="200px"></a><a href="http://www.nxp.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-nxp.png" alt="NXP" width="200px"></a><a href="http://www.qorvo.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-qorvo.png" alt="Qorvo" width="200px"></a><a href="https://www.qualcomm.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-qc.png" alt="Qualcomm" width="200px"></a><a href="https://www.samsung.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-samsung.png" alt="Samsung" width="200px"></a><a href="https://www.silabs.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-silabs.png" alt="Silicon Labs" width="200px"></a><a href="https://www.st.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-stm.png" alt="STMicroelectronics" width="200px"></a><a href="https://www.synopsys.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-synopsys.png" alt="Synopsys" width="200px"></a><a href="https://www.telink-semi.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-telink-github.png" alt="Telink Semiconductor" width="200px"></a><a href="https://www.ti.com/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-ti.png" alt="Texas Instruments" width="200px"></a><a href="https://www.zephyrproject.org/"><img src="https://github.com/openthread/openthread/raw/main/doc/images/ot-contrib-zephyr.png" alt="Zephyr Project" width="200px"></a>
 
 # Getting started
 
@@ -66,10 +66,7 @@
 
 # Need help?
 
-There are numerous avenues for OpenThread support:
+OpenThread support is available on GitHub:
 
 - Bugs and feature requests — [submit to the Issue Tracker](https://github.com/openthread/openthread/issues)
-- Stack Overflow — [post questions using the `openthread` tag](http://stackoverflow.com/questions/tagged/openthread)
-- Google Groups — [discussion and announcements at openthread-users](https://groups.google.com/forum/#!forum/openthread-users)
-
-The openthread-users Google Group is the recommended place for users to discuss OpenThread and interact directly with the OpenThread team.
+- Community Discussion - [ask questions, share ideas, and engage with other community members](https://github.com/openthread/openthread/discussions)
diff --git a/configure.ac b/configure.ac
index 04b18b2..8c0c7fc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -990,6 +990,7 @@
 LIBS="${LIBS} ${NL_COVERAGE_LIBS}"
 LDFLAGS="${LDFLAGS} ${NL_COVERAGE_LDFLAGS}"
 
+CPPFLAGS="${CPPFLAGS} -DOPENTHREAD_CONFIG_FILE='\"openthread-config-generic.h\"'"
 # At this point, we can restore the compiler flags to whatever the
 # user passed in, now that we're clear of an -Werror issues by
 # transforming -Wno-error back to -Werror.
diff --git a/doc/images/ot-contrib-infineon.png b/doc/images/ot-contrib-infineon.png
new file mode 100644
index 0000000..293a52c
--- /dev/null
+++ b/doc/images/ot-contrib-infineon.png
Binary files differ
diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake
index e8c9212..6eec0d6 100644
--- a/etc/cmake/options.cmake
+++ b/etc/cmake/options.cmake
@@ -381,7 +381,6 @@
         target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_LEVEL=OT_LOG_LEVEL_DEBG")
     endif()
     target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL=1")
-    target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_PREPEND_REGION=1")
 endif()
 
 option(OT_OTNS "enable OTNS support")
diff --git a/etc/docker/codelab_otsim/Dockerfile b/etc/docker/codelab_otsim/Dockerfile
deleted file mode 100644
index d7c400f..0000000
--- a/etc/docker/codelab_otsim/Dockerfile
+++ /dev/null
@@ -1,30 +0,0 @@
-# Ubuntu image with tools required to build OpenThread
-FROM ubuntu:18.04
-
-ENV DEBIAN_FRONTEND noninteractive
-ARG OT_GIT_REF=main
-
-# Install dependencies:
-RUN apt-get update -qq
-
-# Install packages needed for build and runtime:
-RUN apt-get --no-install-recommends install -y \
-    git \
-    sudo \
-    inetutils-ping \
-    software-properties-common \
-    ca-certificates \
-    && update-ca-certificates
-
-# Install OpenThread
-RUN mkdir -p ~/src/openthread && \
-    cd ~/src/openthread && \
-    git init . && \
-    git fetch https://github.com/openthread/openthread.git ${OT_GIT_REF} && \
-    git checkout FETCH_HEAD && \
-    ./script/bootstrap && \
-    ./bootstrap && \
-    make -f examples/Makefile-simulation
-
-# Install OpenThread Daemon and ot-ctl
-RUN cd ~/src/openthread && make -f src/posix/Makefile-posix DAEMON=1
diff --git a/etc/gn/openthread.gni b/etc/gn/openthread.gni
index 981d758..68e6ecb 100644
--- a/etc/gn/openthread.gni
+++ b/etc/gn/openthread.gni
@@ -234,6 +234,9 @@
     # Enable builtin mbedtls management
     openthread_config_enable_builtin_mbedtls_management =
         openthread_external_mbedtls == ""
+
+    # Enable radio coexistence
+    openthread_config_coexistence_enable = false
   }
 }
 
diff --git a/include/Makefile.am b/include/Makefile.am
index 6729d68..e91c87c 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -84,6 +84,7 @@
     openthread/srp_server.h               \
     openthread/tasklet.h                  \
     openthread/tcp.h                      \
+    openthread/tcp_ext.h                  \
     openthread/thread.h                   \
     openthread/thread_ftd.h               \
     openthread/trel.h                     \
diff --git a/include/openthread-config-android.h b/include/openthread-config-android.h
deleted file mode 100644
index 9b07c81..0000000
--- a/include/openthread-config-android.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- *  Copyright (c) 2018, The OpenThread Authors.
- *  All rights reserved.
- *
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions are met:
- *  1. Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- *  3. Neither the name of the copyright holder nor the
- *     names of its contributors may be used to endorse or promote products
- *     derived from this software without specific prior written permission.
- *
- *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- *  POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifdef OPENTHREAD_CONFIG_ANDROID_VERSION_HEADER_ENABLE
-#include <openthread-config-android-version.h>
-#endif
-
-/* Define to 1 to enable the border agent feature. */
-#define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 1
-
-/* Define to 1 if you want to enable Border Router */
-#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1
-
-/* Define to 1 if you want to enable channel manager feature */
-#define OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE 0
-
-/* Define to 1 if you want to use channel monitor feature */
-#define OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE 0
-
-/* Define to 1 if you want to use child supervision feature */
-#define OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE 1
-
-/* Define to 1 to enable dtls support. */
-#define OPENTHREAD_CONFIG_DTLS_ENABLE 1
-
-/* Define to 1 if you want to use jam detection feature */
-#define OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE 1
-
-/* Define to 1 to enable the joiner role. */
-#define OPENTHREAD_CONFIG_JOINER_ENABLE 1
-
-/* Define to 1 if you want to use legacy network support */
-#define OPENTHREAD_CONFIG_LEGACY_ENABLE 1
-
-/* Define to 1 to enable the NCP HDLC interface. */
-#define OPENTHREAD_CONFIG_NCP_HDLC_ENABLE 1
-
-/* Define to 1 to enable posix platform. */
-#define OPENTHREAD_PLATFORM_POSIX 1
-
-/* Define to 1 if you want to enable Service */
-#define OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE 1
-
-/* OpenThread examples */
-#define OPENTHREAD_EXAMPLES none
diff --git a/include/openthread-config-fuchsia-legacy-extensions.h b/include/openthread-config-fuchsia-legacy-extensions.h
index 444e188..75ca8c4 100644
--- a/include/openthread-config-fuchsia-legacy-extensions.h
+++ b/include/openthread-config-fuchsia-legacy-extensions.h
@@ -45,5 +45,23 @@
  */
 #define OPENTHREAD_ENABLE_VENDOR_EXTENSION 1
 
+/*
+ * Implementation note:
+ *   These are all "weak" so that a platform may if it chooses to override the instance.
+ */
+
+#if OPENTHREAD_CONFIG_OTNS_ENABLE
+
+#include <openthread/platform/toolchain.h>
+#include "common/log.hpp"
+
+using namespace ot;
+
+OT_TOOL_WEAK
+void otPlatOtnsStatus(const char *aStatus)
+{
+    LogAlways("[OTNS] %s", aStatus);
+}
+#endif // OPENTHREAD_CONFIG_OTNS_ENABLE
 
 #endif  // OPENTHREAD_INCLUDE_OPENTHREAD_CONFIG_FUCHSIA_LEGACY_EXTENSIONS_H_
diff --git a/include/openthread-config-fuchsia.h b/include/openthread-config-fuchsia.h
index bbfe43e..993ab0d 100644
--- a/include/openthread-config-fuchsia.h
+++ b/include/openthread-config-fuchsia.h
@@ -153,7 +153,7 @@
  *
  */
 #ifndef OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS
-#define OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS 8
+#define OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS 32
 #endif
 
 /**
diff --git a/include/openthread/BUILD.gn b/include/openthread/BUILD.gn
index d558fcb..df7035a 100644
--- a/include/openthread/BUILD.gn
+++ b/include/openthread/BUILD.gn
@@ -27,27 +27,10 @@
 
 import("../../etc/gn/openthread.gni")
 
-copy("copy_openthread_config_generic") {
-  sources = [ "../../etc/cmake/openthread-config-generic.h.in" ]
-  outputs = [ "${root_gen_dir}/include/{{source_name_part}}" ]
-}
-
-config("openthread_config_generic_config") {
-  include_dirs = [ "${root_gen_dir}/include" ]
-}
-
-source_set("openthread_config_generic") {
-  sources = [ "${root_gen_dir}/include/openthread-config-generic.h" ]
-  deps = [ ":copy_openthread_config_generic" ]
-  public_configs = [ ":openthread_config_generic_config" ]
-}
-
 source_set("openthread_config") {
   public = [ "config.h" ]
 
-  if (openthread_config_file == "") {
-    public_deps = [ ":openthread_config_generic" ]
-  } else {
+  if (openthread_config_file != "") {
     public_deps = openthread_config_deps
   }
 
@@ -140,6 +123,7 @@
     "srp_server.h",
     "tasklet.h",
     "tcp.h",
+    "tcp_ext.h",
     "thread.h",
     "thread_ftd.h",
     "trel.h",
diff --git a/include/openthread/backbone_router_ftd.h b/include/openthread/backbone_router_ftd.h
index 746d599..a619687 100644
--- a/include/openthread/backbone_router_ftd.h
+++ b/include/openthread/backbone_router_ftd.h
@@ -273,19 +273,6 @@
  */
 otError otBackboneRouterMulticastListenerAdd(otInstance *aInstance, const otIp6Address *aAddress, uint32_t aTimeout);
 
-/**
- * This method configures the ability to increase or not the BBR Dataset Sequence Number when a
- * BBR recovers its BBR Dataset from the Leader's Network Data.
- *
- * Note: available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
- *       Only used for certification.
- *
- * @param[in] aInstance  A pointer to an OpenThread instance.
- * @param[in] aSkip      Whether to skip the increase of Sequence Number or not.
- *
- */
-void otBackboneRouterConfigSkipSeqNumIncrease(otInstance *aInstance, bool aSkip);
-
 #define OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT \
     0 ///< Initializer for otBackboneRouterMulticastListenerIterator
 
@@ -304,12 +291,12 @@
 /**
  * This function gets the next Multicast Listener info (using an iterator).
  *
- * @param[in]     aInstance    A pointer to an OpenThread instance.
- * @param[inout]  aIterator    A pointer to the iterator. On success the iterator will be updated to point to next
- *                             Multicast Listener. To get the first entry the iterator should be set to
- *                             OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT.
- * @param[out]    aListenerInfo  A pointer to an `otBackboneRouterMulticastListenerInfo` where information of next
- *                               Multicast Listener is placed (on success).
+ * @param[in]      aInstance      A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator      A pointer to the iterator. On success the iterator will be updated to point to next
+ *                                Multicast Listener. To get the first entry the iterator should be set to
+ *                                OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT.
+ * @param[out]     aListenerInfo  A pointer to an `otBackboneRouterMulticastListenerInfo` where information of next
+ *                                Multicast Listener is placed (on success).
  *
  * @retval OT_ERROR_NONE       Successfully found the next Multicast Listener info (@p aListenerInfo was successfully
  *                             updated).
diff --git a/include/openthread/border_router.h b/include/openthread/border_router.h
index 4161e19..b09ab20 100644
--- a/include/openthread/border_router.h
+++ b/include/openthread/border_router.h
@@ -137,11 +137,11 @@
 /**
  * This method provides a full or stable copy of the local Thread Network Data.
  *
- * @param[in]     aInstance    A pointer to an OpenThread instance.
- * @param[in]     aStable      TRUE when copying the stable version, FALSE when copying the full version.
- * @param[out]    aData        A pointer to the data buffer.
- * @param[inout]  aDataLength  On entry, size of the data buffer pointed to by @p aData.
- *                             On exit, number of copied bytes.
+ * @param[in]      aInstance    A pointer to an OpenThread instance.
+ * @param[in]      aStable      TRUE when copying the stable version, FALSE when copying the full version.
+ * @param[out]     aData        A pointer to the data buffer.
+ * @param[in,out]  aDataLength  On entry, size of the data buffer pointed to by @p aData.
+ *                              On exit, number of copied bytes.
  */
 otError otBorderRouterGetNetData(otInstance *aInstance, bool aStable, uint8_t *aData, uint8_t *aDataLength);
 
@@ -177,10 +177,10 @@
 /**
  * This function gets the next On Mesh Prefix in the local Network Data.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the Network Data iterator context. To get the first on-mesh entry
-                             it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
- * @param[out]    aConfig    A pointer to the On Mesh Prefix information.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the Network Data iterator context. To get the first on-mesh entry
+                              it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
+ * @param[out]     aConfig    A pointer to the On Mesh Prefix information.
  *
  * @retval OT_ERROR_NONE       Successfully found the next On Mesh prefix.
  * @retval OT_ERROR_NOT_FOUND  No subsequent On Mesh prefix exists in the Thread Network Data.
@@ -222,10 +222,10 @@
 /**
  * This function gets the next external route in the local Network Data.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the Network Data iterator context. To get the first external route entry
-                             it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
- * @param[out]    aConfig    A pointer to the External Route information.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the Network Data iterator context. To get the first external route entry
+                              it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
+ * @param[out]     aConfig    A pointer to the External Route information.
  *
  * @retval OT_ERROR_NONE       Successfully found the next External Route.
  * @retval OT_ERROR_NOT_FOUND  No subsequent external route entry exists in the Thread Network Data.
diff --git a/include/openthread/cli.h b/include/openthread/cli.h
index 56c9868..3fa5c56 100644
--- a/include/openthread/cli.h
+++ b/include/openthread/cli.h
@@ -70,9 +70,9 @@
 /**
  * This function pointer is called to notify about Console output.
  *
- * @param[in]  aBuf        A pointer to a buffer with an output.
- * @param[in]  aBufLength  A length of the output data stored in the buffer.
  * @param[out] aContext    A user context pointer.
+ * @param[in]  aFormat     The format string.
+ * @param[in]  aArguments  The format string arguments.
  *
  * @returns                Number of bytes written by the callback.
  *
@@ -147,16 +147,6 @@
 void otCliPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs);
 
 /**
- * Function to write the OpenThread Log to the CLI console.
- *
- * @param[in]  aLogLevel   The log level.
- * @param[in]  aLogRegion  The log region.
- * @param[in]  aLogLine    A pointer to the log line string.
- *
- */
-void otCliPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine);
-
-/**
  * @}
  *
  */
diff --git a/include/openthread/coap.h b/include/openthread/coap.h
index 396d535..c2e036b 100644
--- a/include/openthread/coap.h
+++ b/include/openthread/coap.h
@@ -389,9 +389,9 @@
  * is enabled.
  *
  * @param[in]       aContext     A pointer to application-specific context.
- * @param[inout]    aBlock       A pointer to where the block segment can be written to.
+ * @param[in,out]   aBlock       A pointer to where the block segment can be written to.
  * @param[in]       aPosition    The position in a sequence from which to obtain the block segment.
- * @param[inout]    aBlockLength On entry, the maximum block segment length in bytes.
+ * @param[in,out]   aBlockLength On entry, the maximum block segment length in bytes.
  * @param[out]      aMore        A pointer to the flag if more block segments will follow.
  *
  * @warning By changing the value of aBlockLength, the block size of the whole exchange is
@@ -485,9 +485,9 @@
 /**
  * This function initializes the CoAP header.
  *
- * @param[inout] aMessage   A pointer to the CoAP message to initialize.
- * @param[in]    aType      CoAP message type.
- * @param[in]    aCode      CoAP message code.
+ * @param[in,out] aMessage   A pointer to the CoAP message to initialize.
+ * @param[in]     aType      CoAP message type.
+ * @param[in]     aCode      CoAP message code.
  *
  */
 void otCoapMessageInit(otMessage *aMessage, otCoapType aType, otCoapCode aCode);
@@ -497,10 +497,10 @@
  *
  * @note Both message ID and token are set according to @p aRequest.
  *
- * @param[inout] aResponse  A pointer to the CoAP response message.
- * @param[in]    aRequest   A pointer to the CoAP request message.
- * @param[in]    aType      CoAP message type.
- * @param[in]    aCode      CoAP message code.
+ * @param[in,out]  aResponse  A pointer to the CoAP response message.
+ * @param[in]      aRequest   A pointer to the CoAP request message.
+ * @param[in]      aType      CoAP message type.
+ * @param[in]      aCode      CoAP message code.
  *
  * @retval OT_ERROR_NONE     Successfully initialized the response message.
  * @retval OT_ERROR_NO_BUFS  Insufficient message buffers available to initialize the response message.
@@ -511,9 +511,9 @@
 /**
  * This function sets the Token value and length in a header.
  *
- * @param[inout]  aMessage          A pointer to the CoAP message.
- * @param[in]     aToken            A pointer to the Token value.
- * @param[in]     aTokenLength      The Length of @p aToken.
+ * @param[in,out]  aMessage          A pointer to the CoAP message.
+ * @param[in]      aToken            A pointer to the Token value.
+ * @param[in]      aTokenLength      The Length of @p aToken.
  *
  * @retval OT_ERROR_NONE     Successfully set the Token value.
  * @retval OT_ERROR_NO_BUFS  Insufficient buffers to set the Token value.
@@ -524,8 +524,8 @@
 /**
  * This function sets the Token length and randomizes its value.
  *
- * @param[inout]  aMessage      A pointer to the CoAP message.
- * @param[in]     aTokenLength  The Length of a Token to set.
+ * @param[in,out]  aMessage      A pointer to the CoAP message.
+ * @param[in]      aTokenLength  The Length of a Token to set.
  *
  */
 void otCoapMessageGenerateToken(otMessage *aMessage, uint8_t aTokenLength);
@@ -540,8 +540,8 @@
  * and if the desired format type code isn't listed in otCoapOptionContentFormat,
  * this base function should be used instead.
  *
- * @param[inout]  aMessage          A pointer to the CoAP message.
- * @param[in]     aContentFormat    One of the content formats listed in
+ * @param[in,out]  aMessage          A pointer to the CoAP message.
+ * @param[in]      aContentFormat    One of the content formats listed in
  *                                  otCoapOptionContentFormat above.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
@@ -554,10 +554,10 @@
 /**
  * This function appends a CoAP option in a header.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aNumber   The CoAP Option number.
- * @param[in]     aLength   The CoAP Option length.
- * @param[in]     aValue    A pointer to the CoAP value.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aNumber   The CoAP Option number.
+ * @param[in]      aLength   The CoAP Option length.
+ * @param[in]      aValue    A pointer to the CoAP value.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -570,9 +570,9 @@
  * This function appends an unsigned integer CoAP option as specified in
  * https://tools.ietf.org/html/rfc7252#section-3.2
  *
- * @param[inout]  aMessage A pointer to the CoAP message.
- * @param[in]     aNumber  The CoAP Option number.
- * @param[in]     aValue   The CoAP Option unsigned integer value.
+ * @param[in,out]  aMessage A pointer to the CoAP message.
+ * @param[in]      aNumber  The CoAP Option number.
+ * @param[in]      aValue   The CoAP Option unsigned integer value.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -585,8 +585,8 @@
 /**
  * This function appends an Observe option.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aObserve  Observe field value.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aObserve  Observe field value.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -598,8 +598,8 @@
 /**
  * This function appends a Uri-Path option.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aUriPath  A pointer to a NULL-terminated string.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aUriPath  A pointer to a NULL-terminated string.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -621,10 +621,10 @@
 /**
  * This function appends a Block2 option
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aNum      Current block number.
- * @param[in]     aMore     Boolean to indicate more blocks are to be sent.
- * @param[in]     aSize     Block Size Exponent.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aNum      Current block number.
+ * @param[in]      aMore     Boolean to indicate more blocks are to be sent.
+ * @param[in]      aSize     Block Size Exponent.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -636,10 +636,10 @@
 /**
  * This function appends a Block1 option
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aNum      Current block number.
- * @param[in]     aMore     Boolean to indicate more blocks are to be sent.
- * @param[in]     aSize     Block Size Exponent.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aNum      Current block number.
+ * @param[in]      aMore     Boolean to indicate more blocks are to be sent.
+ * @param[in]      aSize     Block Size Exponent.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -651,8 +651,8 @@
 /**
  * This function appends a Proxy-Uri option.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aUriPath  A pointer to a NULL-terminated string.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aUriPath  A pointer to a NULL-terminated string.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -664,8 +664,8 @@
 /**
  * This function appends a Max-Age option.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aMaxAge   The Max-Age value.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aMaxAge   The Max-Age value.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -677,8 +677,8 @@
 /**
  * This function appends a single Uri-Query option.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
- * @param[in]     aUriQuery A pointer to NULL-terminated string, which should contain a single key=value pair.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
+ * @param[in]      aUriQuery A pointer to NULL-terminated string, which should contain a single key=value pair.
  *
  * @retval OT_ERROR_NONE          Successfully appended the option.
  * @retval OT_ERROR_INVALID_ARGS  The option type is not equal or greater than the last option type.
@@ -689,7 +689,7 @@
 /**
  * This function adds Payload Marker indicating beginning of the payload to the CoAP header.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message.
+ * @param[in,out]  aMessage  A pointer to the CoAP message.
  *
  * @retval OT_ERROR_NONE     Payload Marker successfully added.
  * @retval OT_ERROR_NO_BUFS  Header Payload Marker exceeds the buffer size.
@@ -720,8 +720,8 @@
 /**
  * This function sets the Code value.
  *
- * @param[inout]  aMessage  A pointer to the CoAP message to initialize.
- * @param[in]     aCode     CoAP message code.
+ * @param[in,out]  aMessage  A pointer to the CoAP message to initialize.
+ * @param[in]      aCode     CoAP message code.
  *
  */
 void otCoapMessageSetCode(otMessage *aMessage, otCoapCode aCode);
@@ -769,8 +769,8 @@
 /**
  * This function initialises an iterator for the options in the given message.
  *
- * @param[inout]  aIterator A pointer to the CoAP message option iterator.
- * @param[in]     aMessage  A pointer to the CoAP message.
+ * @param[in,out]  aIterator A pointer to the CoAP message option iterator.
+ * @param[in]      aMessage  A pointer to the CoAP message.
  *
  * @retval  OT_ERROR_NONE   Successfully initialised.
  * @retval  OT_ERROR_PARSE  Message state is inconsistent.
@@ -792,7 +792,7 @@
 /**
  * This function returns a pointer to the first option.
  *
- * @param[inout]  aIterator A pointer to the CoAP message option iterator.
+ * @param[in,out]  aIterator A pointer to the CoAP message option iterator.
  *
  * @returns A pointer to the first option. If no option is present NULL pointer is returned.
  *
@@ -813,7 +813,7 @@
 /**
  * This function returns a pointer to the next option.
  *
- * @param[inout]  aIterator A pointer to the CoAP message option iterator.
+ * @param[in,out]  aIterator A pointer to the CoAP message option iterator.
  *
  * @returns A pointer to the next option. If no more options are present NULL pointer is returned.
  *
@@ -824,7 +824,7 @@
  * This function fills current option value into @p aValue assuming the current value is an unsigned integer encoded
  * according to https://tools.ietf.org/html/rfc7252#section-3.2
  *
- * @param[inout]    aIterator   A pointer to the CoAP message option iterator.
+ * @param[in,out]   aIterator   A pointer to the CoAP message option iterator.
  * @param[out]      aValue      A pointer to an unsigned integer to receive the option value.
  *
  * @retval  OT_ERROR_NONE       Successfully filled value.
@@ -838,8 +838,8 @@
 /**
  * This function fills current option value into @p aValue.
  *
- * @param[inout]  aIterator A pointer to the CoAP message option iterator.
- * @param[out]    aValue    A pointer to a buffer to receive the option value.
+ * @param[in,out]  aIterator A pointer to the CoAP message option iterator.
+ * @param[out]     aValue    A pointer to a buffer to receive the option value.
  *
  * @retval  OT_ERROR_NONE       Successfully filled value.
  * @retval  OT_ERROR_NOT_FOUND  No current option.
diff --git a/include/openthread/commissioner.h b/include/openthread/commissioner.h
index f142edc..d632fd0 100644
--- a/include/openthread/commissioner.h
+++ b/include/openthread/commissioner.h
@@ -154,10 +154,8 @@
 /**
  * This function pointer is called whenever the commissioner state changes.
  *
- * @param[in]  aChannelMask       The channel mask value.
- * @param[in]  aEnergyList        A pointer to the energy measurement list.
- * @param[in]  aEnergyListLength  Number of entries in @p aEnergyListLength.
- * @param[in]  aContext           A pointer to application-specific context.
+ * @param[in]  aState    The Commissioner state.
+ * @param[in]  aContext  A pointer to application-specific context.
  *
  */
 typedef void (*otCommissionerStateCallback)(otCommissionerState aState, void *aContext);
@@ -206,6 +204,29 @@
 otError otCommissionerStop(otInstance *aInstance);
 
 /**
+ * This function returns the Commissioner Id.
+ *
+ * @param[in]  aInstance         A pointer to an OpenThread instance.
+ *
+ * @returns The Commissioner Id.
+ *
+ */
+const char *otCommissionerGetId(otInstance *aInstance);
+
+/**
+ * This function sets the Commissioner Id.
+ *
+ * @param[in]  aInstance     A pointer to an OpenThread instance.
+ * @param[in]  aId           A pointer to a string character array. Must be null terminated.
+ *
+ * @retval OT_ERROR_NONE            Successfully set the Commissioner Id.
+ * @retval OT_ERROR_INVALID_ARGS    Given name is too long.
+ * @retval OT_ERROR_INVALID_STATE   The commissioner is active and id cannot be changed.
+ *
+ */
+otError otCommissionerSetId(otInstance *aInstance, const char *aId);
+
+/**
  * This function adds a Joiner entry.
  *
  * @param[in]  aInstance          A pointer to an OpenThread instance.
@@ -251,7 +272,7 @@
  * This method get joiner info at aIterator position.
  *
  * @param[in]      aInstance   A pointer to instance.
- * @param[inout]   aIterator   A pointer to the Joiner Info iterator context.
+ * @param[in,out]  aIterator   A pointer to the Joiner Info iterator context.
  * @param[out]     aJoiner     A reference to Joiner info.
  *
  * @retval OT_ERROR_NONE       Successfully get the Joiner info.
@@ -280,7 +301,7 @@
  * This function removes a Joiner entry.
  *
  * @param[in]  aInstance          A pointer to an OpenThread instance.
- * @param[in]  aEui64             A pointer to the Joiner Discerner.
+ * @param[in]  aDiscerner         A pointer to the Joiner Discerner.
  *
  * @retval OT_ERROR_NONE          Successfully removed the Joiner.
  * @retval OT_ERROR_NOT_FOUND     The Joiner specified by @p aEui64 was not found.
diff --git a/include/openthread/config.h b/include/openthread/config.h
index 2a5fd26..090532c 100644
--- a/include/openthread/config.h
+++ b/include/openthread/config.h
@@ -41,10 +41,8 @@
  * The OpenThread feature configuration file.
  *
  */
-#if !defined(OPENTHREAD_CONFIG_FILE)
-#define OPENTHREAD_CONFIG_FILE <openthread-config-generic.h>
-#endif
-
+#if defined(OPENTHREAD_CONFIG_FILE)
 #include OPENTHREAD_CONFIG_FILE
+#endif
 
 #endif // OPENTHREAD_CONFIG_H_
diff --git a/include/openthread/crypto.h b/include/openthread/crypto.h
index a1a3d36..3be657b 100644
--- a/include/openthread/crypto.h
+++ b/include/openthread/crypto.h
@@ -89,20 +89,20 @@
 /**
  * This method performs AES CCM computation.
  *
- * @param[in]     aKey           A pointer to the key.
- * @param[in]     aTagLength     Length of tag in bytes.
- * @param[in]     aNonce         A pointer to the nonce.
- * @param[in]     aNonceLength   Length of nonce in bytes.
+ * @param[in]      aKey           A pointer to the key.
+ * @param[in]      aTagLength     Length of tag in bytes.
+ * @param[in]      aNonce         A pointer to the nonce.
+ * @param[in]      aNonceLength   Length of nonce in bytes.
  *
- * @param[in]     aHeader        A pointer to the header.
- * @param[in]     aHeaderLength  Length of header in bytes.
+ * @param[in]      aHeader        A pointer to the header.
+ * @param[in]      aHeaderLength  Length of header in bytes.
  *
- * @param[inout]  aPlainText     A pointer to the plaintext.
- * @param[inout]  aCipherText    A pointer to the ciphertext.
- * @param[in]     aLength        Plaintext length in bytes.
- * @param[in]     aEncrypt       `true` on encrypt and `false` on decrypt.
+ * @param[in,out]  aPlainText     A pointer to the plaintext.
+ * @param[in,out]  aCipherText    A pointer to the ciphertext.
+ * @param[in]      aLength        Plaintext length in bytes.
+ * @param[in]      aEncrypt       `true` on encrypt and `false` on decrypt.
  *
- * @param[out]    aTag           A pointer to the tag.
+ * @param[out]     aTag           A pointer to the tag.
  *
  */
 void otCryptoAesCcm(const otCryptoKey *aKey,
@@ -120,12 +120,12 @@
 /**
  * This method creates ECDSA sign.
  *
- * @param[out]    aOutput            An output buffer where ECDSA sign should be stored.
- * @param[inout]  aOutputLength      The length of the @p aOutput buffer.
- * @param[in]     aInputHash         An input hash.
- * @param[in]     aInputHashLength   The length of the @p aInputHash buffer.
- * @param[in]     aPrivateKey        A private key in PEM format.
- * @param[in]     aPrivateKeyLength  The length of the @p aPrivateKey buffer.
+ * @param[out]     aOutput            An output buffer where ECDSA sign should be stored.
+ * @param[in,out]  aOutputLength      The length of the @p aOutput buffer.
+ * @param[in]      aInputHash         An input hash.
+ * @param[in]      aInputHashLength   The length of the @p aInputHash buffer.
+ * @param[in]      aPrivateKey        A private key in PEM format.
+ * @param[in]      aPrivateKeyLength  The length of the @p aPrivateKey buffer.
  *
  * @retval  OT_ERROR_NONE         ECDSA sign has been created successfully.
  * @retval  OT_ERROR_NO_BUFS      Output buffer is too small.
diff --git a/include/openthread/dataset.h b/include/openthread/dataset.h
index 6e899b1..a92f3a7 100644
--- a/include/openthread/dataset.h
+++ b/include/openthread/dataset.h
@@ -152,7 +152,6 @@
     bool    mNativeCommissioningEnabled : 1;     ///< Native Commissioning using PSKc is allowed
     bool    mRoutersEnabled : 1;                 ///< Thread 1.0/1.1.x Routers are enabled
     bool    mExternalCommissioningEnabled : 1;   ///< External Commissioner authentication is allowed
-    bool    mBeaconsEnabled : 1;                 ///< Thread 1.0/1.1.x Beacons are enabled
     bool    mCommercialCommissioningEnabled : 1; ///< Commercial Commissioning is enabled
     bool    mAutonomousEnrollmentEnabled : 1;    ///< Autonomous Enrollment is enabled
     bool    mNetworkKeyProvisioningEnabled : 1;  ///< Network Key Provisioning is enabled
diff --git a/include/openthread/dns_client.h b/include/openthread/dns_client.h
index e212062..17817c1 100644
--- a/include/openthread/dns_client.h
+++ b/include/openthread/dns_client.h
@@ -199,6 +199,8 @@
  *
  * @retval OT_ERROR_NONE          Query sent successfully. @p aCallback will be invoked to report the status.
  * @retval OT_ERROR_NO_BUFS       Insufficient buffer to prepare and send query.
+ * @retval OT_ERROR_INVALID_ARGS  The host name is not valid format.
+ * @retval OT_ERROR_INVALID_STATE Cannot send query since Thread interface is not up.
  *
  */
 otError otDnsClientResolveAddress(otInstance *            aInstance,
@@ -208,6 +210,36 @@
                                   const otDnsQueryConfig *aConfig);
 
 /**
+ * This function sends an address resolution DNS query for A (IPv4) record(s) for a given host name.
+ *
+ * This function requires and is available when `OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE` is enabled.
+ *
+ * When a successful response is received, the addresses are returned from @p aCallback as NAT64 IPv6 translated
+ * versions of the IPv4 addresses from the query response.
+ *
+ * The @p aConfig can be NULL. In this case the default config (from `otDnsClientGetDefaultConfig()`) will be used as
+ * the config for this query. In a non-NULL @p aConfig, some of the fields can be left unspecified (value zero). The
+ * unspecified fields are then replaced by the values from the default config.
+ *
+ * @param[in]  aInstance        A pointer to an OpenThread instance.
+ * @param[in]  aHostName        The host name for which to query the address (MUST NOT be NULL).
+ * @param[in]  aCallback        A function pointer that shall be called on response reception or time-out.
+ * @param[in]  aContext         A pointer to arbitrary context information.
+ * @param[in]  aConfig          A pointer to the config to use for this query.
+ *
+ * @retval OT_ERROR_NONE          Query sent successfully. @p aCallback will be invoked to report the status.
+ * @retval OT_ERROR_NO_BUFS       Insufficient buffer to prepare and send query.
+ * @retval OT_ERROR_INVALID_ARGS  The host name is not valid format or NAT64 is not enabled in config.
+ * @retval OT_ERROR_INVALID_STATE Cannot send query since Thread interface is not up.
+ *
+ */
+otError otDnsClientResolveIp4Address(otInstance *            aInstance,
+                                     const char *            aHostName,
+                                     otDnsAddressCallback    aCallback,
+                                     void *                  aContext,
+                                     const otDnsQueryConfig *aConfig);
+
+/**
  * This function gets the full host name associated with an address resolution DNS response.
  *
  * This function MUST only be used from `otDnsAddressCallback`.
@@ -239,9 +271,10 @@
  * @param[out] aTtl          A pointer to an `uint32_t` to output TTL for the address. It can be NULL if caller does not
  *                           want to get the TTL.
  *
- * @retval OT_ERROR_NONE       The address was read successfully.
- * @retval OT_ERROR_NOT_FOUND  No address record in @p aResponse at @p aIndex.
- * @retval OT_ERROR_PARSE      Could not parse the records in the @p aResponse.
+ * @retval OT_ERROR_NONE           The address was read successfully.
+ * @retval OT_ERROR_NOT_FOUND      No address record in @p aResponse at @p aIndex.
+ * @retval OT_ERROR_PARSE          Could not parse the records in the @p aResponse.
+ * @retval OT_ERROR_INVALID_STATE  No NAT64 prefix (applicable only when NAT64 is allowed).
  *
  */
 otError otDnsAddressResponseGetAddress(const otDnsAddressResponse *aResponse,
diff --git a/include/openthread/history_tracker.h b/include/openthread/history_tracker.h
index dfb098f..a8dc7c3 100644
--- a/include/openthread/history_tracker.h
+++ b/include/openthread/history_tracker.h
@@ -241,12 +241,12 @@
 /**
  * This function iterates over the entries in the network info history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
- *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
- *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
- *                           older than max age.
+ * @param[in]     aInstance   A pointer to the OpenThread instance.
+ * @param[in,out] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ *                            Age is provided as the duration (in milliseconds) from when entry was recorded to
+ *                            @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
+ *                            older than max age.
  *
  * @returns A pointer to `otHistoryTrackerNetworkInfo` entry or `NULL` if no more entries in the list.
  *
@@ -258,9 +258,9 @@
 /**
  * This function iterates over the entries in the unicast address history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ * @param[in]     aInstance  A pointer to the OpenThread instance.
+ * @param[in,out] aIterator  A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge  A pointer to a variable to output the entry's age. MUST NOT be NULL.
  *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
  *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
  *                           older than max age.
@@ -276,9 +276,9 @@
 /**
  * This function iterates over the entries in the multicast address history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ * @param[in]     aInstance  A pointer to the OpenThread instance.
+ * @param[in,out] aIterator  A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge  A pointer to a variable to output the entry's age. MUST NOT be NULL.
  *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
  *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
  *                           older than max age.
@@ -294,9 +294,9 @@
 /**
  * This function iterates over the entries in the RX message history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ * @param[in]     aInstance  A pointer to the OpenThread instance.
+ * @param[in,out] aIterator  A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge  A pointer to a variable to output the entry's age. MUST NOT be NULL.
  *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
  *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
  *                           older than max age.
@@ -311,9 +311,9 @@
 /**
  * This function iterates over the entries in the TX message history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ * @param[in]     aInstance  A pointer to the OpenThread instance.
+ * @param[in,out] aIterator  A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge  A pointer to a variable to output the entry's age. MUST NOT be NULL.
  *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
  *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
  *                           older than max age.
@@ -328,9 +328,9 @@
 /**
  * This function iterates over the entries in the neighbor history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ * @param[in]     aInstance  A pointer to the OpenThread instance.
+ * @param[in,out] aIterator  A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge  A pointer to a variable to output the entry's age. MUST NOT be NULL.
  *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
  *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
  *                           older than max age.
@@ -345,9 +345,9 @@
 /**
  * This function iterates over the entries in the Network Data on mesh prefix entry history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ * @param[in]     aInstance  A pointer to the OpenThread instance.
+ * @param[in,out] aIterator  A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge  A pointer to a variable to output the entry's age. MUST NOT be NULL.
  *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
  *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
  *                           older than max age.
@@ -362,9 +362,9 @@
 /**
  * This function iterates over the entries in the Network Data external route entry history list.
  *
- * @param[in]    aInstance   A pointer to the OpenThread instance.
- * @param[inout] aIterator   A pointer to an iterator. MUST be initialized or the behavior is undefined.
- * @param[out]   aEntryAge   A pointer to a variable to output the entry's age. MUST NOT be NULL.
+ * @param[in]     aInstance  A pointer to the OpenThread instance.
+ * @param[in,out] aIterator  A pointer to an iterator. MUST be initialized or the behavior is undefined.
+ * @param[out]    aEntryAge  A pointer to a variable to output the entry's age. MUST NOT be NULL.
  *                           Age is provided as the duration (in milliseconds) from when entry was recorded to
  *                           @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries
  *                           older than max age.
diff --git a/include/openthread/instance.h b/include/openthread/instance.h
index 271378e..9fe27ff 100644
--- a/include/openthread/instance.h
+++ b/include/openthread/instance.h
@@ -53,7 +53,7 @@
  * @note This number versions both OpenThread platform and user APIs.
  *
  */
-#define OPENTHREAD_API_VERSION (190)
+#define OPENTHREAD_API_VERSION (206)
 
 /**
  * @addtogroup api-instance
@@ -78,9 +78,9 @@
  *
  * This function is available and can only be used when support for multiple OpenThread instances is enabled.
  *
- * @param[in]    aInstanceBuffer      The buffer for OpenThread to use for allocating the otInstance structure.
- * @param[inout] aInstanceBufferSize  On input, the size of aInstanceBuffer. On output, if not enough space for
- *                                    otInstance, the number of bytes required for otInstance.
+ * @param[in]     aInstanceBuffer      The buffer for OpenThread to use for allocating the otInstance structure.
+ * @param[in,out] aInstanceBufferSize  On input, the size of aInstanceBuffer. On output, if not enough space for
+ *                                     otInstance, the number of bytes required for otInstance.
  *
  * @returns  A pointer to the new OpenThread instance.
  *
diff --git a/include/openthread/ip6.h b/include/openthread/ip6.h
index 62f18e9..00f20b0 100644
--- a/include/openthread/ip6.h
+++ b/include/openthread/ip6.h
@@ -344,8 +344,9 @@
  * @param[in]  aAddress  A pointer to an IP Address.
  *
  * @retval OT_ERROR_NONE          Successfully unsubscribed to the Network Interface Multicast Address.
- * @retval OT_ERROR_INVALID_ARGS  The IP Address indicated by @p aAddress is an internal address.
+ * @retval OT_ERROR_REJECTED      The IP Address indicated by @p aAddress is an internal address.
  * @retval OT_ERROR_NOT_FOUND     The IP Address indicated by @p aAddress was not found.
+ *
  */
 otError otIp6UnsubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress);
 
@@ -692,8 +693,8 @@
 /**
  * This function perform OpenThread source address selection.
  *
- * @param[in]     aInstance     A pointer to an OpenThread instance.
- * @param[inout]  aMessageInfo  A pointer to the message information.
+ * @param[in]      aInstance     A pointer to an OpenThread instance.
+ * @param[in,out]  aMessageInfo  A pointer to the message information.
  *
  * @retval  OT_ERROR_NONE       Found a source address and is filled into mSockAddr of @p aMessageInfo.
  * @retval  OT_ERROR_NOT_FOUND  No source address was found and @p aMessageInfo is unchanged.
@@ -733,7 +734,7 @@
  * when it is about to add a SLAAC address based on a prefix. Its boolean return value determines whether the address
  * is filtered (not added) or not.
  *
- * @param[in] aInstacne   A pointer to an OpenThread instance.
+ * @param[in] aInstance   A pointer to an OpenThread instance.
  * @param[in] aPrefix     A pointer to prefix for which SLAAC address is about to be added.
  *
  * @retval TRUE    Indicates that the SLAAC address based on the prefix should be filtered and NOT added.
diff --git a/include/openthread/link.h b/include/openthread/link.h
index 1a6b0a8..4be9a79 100644
--- a/include/openthread/link.h
+++ b/include/openthread/link.h
@@ -60,11 +60,12 @@
  */
 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; ///< Indicates whether or not link security is enabled.
+    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.
@@ -378,18 +379,18 @@
  */
 typedef struct otActiveScanResult
 {
-    otExtAddress    mExtAddress;     ///< IEEE 802.15.4 Extended Address
-    otNetworkName   mNetworkName;    ///< Thread Network Name
-    otExtendedPanId mExtendedPanId;  ///< Thread Extended PAN ID
-    otSteeringData  mSteeringData;   ///< Steering Data
-    uint16_t        mPanId;          ///< IEEE 802.15.4 PAN ID
-    uint16_t        mJoinerUdpPort;  ///< Joiner UDP Port
-    uint8_t         mChannel;        ///< IEEE 802.15.4 Channel
-    int8_t          mRssi;           ///< RSSI (dBm)
-    uint8_t         mLqi;            ///< LQI
-    unsigned int    mVersion : 4;    ///< Version
-    bool            mIsNative : 1;   ///< Native Commissioner flag
-    bool            mIsJoinable : 1; ///< Joining Permitted flag
+    otExtAddress    mExtAddress;    ///< IEEE 802.15.4 Extended Address
+    otNetworkName   mNetworkName;   ///< Thread Network Name
+    otExtendedPanId mExtendedPanId; ///< Thread Extended PAN ID
+    otSteeringData  mSteeringData;  ///< Steering Data
+    uint16_t        mPanId;         ///< IEEE 802.15.4 PAN ID
+    uint16_t        mJoinerUdpPort; ///< Joiner UDP Port
+    uint8_t         mChannel;       ///< IEEE 802.15.4 Channel
+    int8_t          mRssi;          ///< RSSI (dBm)
+    uint8_t         mLqi;           ///< LQI
+    unsigned int    mVersion : 4;   ///< Version
+    bool            mIsNative : 1;  ///< Native Commissioner flag
+    bool            mDiscover : 1;  ///< Result from MLE Discovery
 } otActiveScanResult;
 
 /**
@@ -772,10 +773,10 @@
  *
  * This function is available when OPENTHREAD_CONFIG_MAC_FILTER_ENABLE configuration is enabled.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the MAC filter iterator context. To get the first in-use address filter entry,
- *                           it should be set to OT_MAC_FILTER_ITERATOR_INIT. MUST NOT be NULL.
- * @param[out]    aEntry     A pointer to where the information is placed. MUST NOT be NULL.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the MAC filter iterator context. To get the first in-use address filter
+ *                            entry, it should be set to OT_MAC_FILTER_ITERATOR_INIT. MUST NOT be NULL.
+ * @param[out]     aEntry     A pointer to where the information is placed. MUST NOT be NULL.
  *
  * @retval OT_ERROR_NONE          Successfully retrieved an in-use address filter entry.
  * @retval OT_ERROR_NOT_FOUND     No subsequent entry exists.
@@ -851,12 +852,12 @@
  *
  * This function is available when OPENTHREAD_CONFIG_MAC_FILTER_ENABLE configuration is enabled.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the MAC filter iterator context. MUST NOT be NULL.
- *                           To get the first entry, it should be set to OT_MAC_FILTER_ITERATOR_INIT.
- * @param[out]    aEntry     A pointer to where the information is placed. The last entry would have the extended
- *                           address as all 0xff to indicate the default received signal strength if it was set.
-                             @p aEntry MUST NOT be NULL.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the MAC filter iterator context. MUST NOT be NULL.
+ *                            To get the first entry, it should be set to OT_MAC_FILTER_ITERATOR_INIT.
+ * @param[out]     aEntry     A pointer to where the information is placed. The last entry would have the extended
+ *                            address as all 0xff to indicate the default received signal strength if it was set.
+                              @p aEntry MUST NOT be NULL.
  *
  * @retval OT_ERROR_NONE          Successfully retrieved the next entry.
  * @retval OT_ERROR_NOT_FOUND     No subsequent entry exists.
diff --git a/include/openthread/logging.h b/include/openthread/logging.h
index b406301..b07e643 100644
--- a/include/openthread/logging.h
+++ b/include/openthread/logging.h
@@ -77,6 +77,144 @@
 otError otLoggingSetLevel(otLogLevel aLogLevel);
 
 /**
+ * This function emits a log message at critical log level.
+ *
+ * This function is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log
+ * level is below critical, this function does not emit any log message.
+ *
+ * @param[in]  aFormat  The format string.
+ * @param[in]  ...      Arguments for the format specification.
+ *
+ */
+void otLogCritPlat(const char *aFormat, ...);
+
+/**
+ * This function emits a log message at warning log level.
+ *
+ * This function is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log
+ * level is below warning, this function does not emit any log message.
+ *
+ * @param[in]  aFormat  The format string.
+ * @param[in]  ...      Arguments for the format specification.
+ *
+ */
+void otLogWarnPlat(const char *aFormat, ...);
+
+/**
+ * This function emits a log message at note log level.
+ *
+ * This function is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log
+ * level is below note, this function does not emit any log message.
+ *
+ * @param[in]  aFormat  The format string.
+ * @param[in]  ...      Arguments for the format specification.
+ *
+ */
+void otLogNotePlat(const char *aFormat, ...);
+
+/**
+ * This function emits a log message at info log level.
+ *
+ * This function is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log
+ * level is below info, this function does not emit any log message.
+ *
+ * @param[in]  aFormat  The format string.
+ * @param[in]  ...      Arguments for the format specification.
+ *
+ */
+void otLogInfoPlat(const char *aFormat, ...);
+
+/**
+ * This function emits a log message at debug log level.
+ *
+ * This function is intended for use by platform. If `OPENTHREAD_CONFIG_LOG_PLATFORM` is not set or the current log
+ * level is below debug, this function does not emit any log message.
+ *
+ * @param[in]  aFormat  The format string.
+ * @param[in]  ...      Arguments for the format specification.
+ *
+ */
+void otLogDebgPlat(const char *aFormat, ...);
+
+/**
+ * This function generates a memory dump at critical log level.
+ *
+ * If `OPENTHREAD_CONFIG_LOG_PLATFORM` or `OPENTHREAD_CONFIG_LOG_PKT_DUMP` is not set or the current log level is below
+ * critical this function does not emit any log message.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+void otDumpCritPlat(const char *aText, const void *aData, uint16_t aDataLength);
+
+/**
+ * This function generates a memory dump at warning log level.
+ *
+ * If `OPENTHREAD_CONFIG_LOG_PLATFORM` or `OPENTHREAD_CONFIG_LOG_PKT_DUMP` is not set or the current log level is below
+ * warning this function does not emit any log message.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+void otDumpWarnPlat(const char *aText, const void *aData, uint16_t aDataLength);
+
+/**
+ * This function generates a memory dump at note log level.
+ *
+ * If `OPENTHREAD_CONFIG_LOG_PLATFORM` or `OPENTHREAD_CONFIG_LOG_PKT_DUMP` is not set or the current log level is below
+ * note this function does not emit any log message.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+void otDumpNotePlat(const char *aText, const void *aData, uint16_t aDataLength);
+
+/**
+ * This function generates a memory dump at info log level.
+ *
+ * If `OPENTHREAD_CONFIG_LOG_PLATFORM` or `OPENTHREAD_CONFIG_LOG_PKT_DUMP` is not set or the current log level is below
+ * info this function does not emit any log message.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+void otDumpInfoPlat(const char *aText, const void *aData, uint16_t aDataLength);
+
+/**
+ * This function generates a memory dump at debug log level.
+ *
+ * If `OPENTHREAD_CONFIG_LOG_PLATFORM` or `OPENTHREAD_CONFIG_LOG_PKT_DUMP` is not set or the current log level is below
+ * debug this function does not emit any log message.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+void otDumpDebgPlat(const char *aText, const void *aData, uint16_t aDataLength);
+
+/**
+ * This function emits a log message at a given log level.
+ *
+ * This function is intended for use by CLI only. If `OPENTHREAD_CONFIG_LOG_CLI` is not set or the current log
+ * level is below the given log level, this function does not emit any log message.
+ *
+ * @param[in]  aLogLevel The log level.
+ * @param[in]  aFormat   The format string.
+ * @param[in]  ...       Arguments for the format specification.
+ *
+ */
+void otLogCli(otLogLevel aLogLevel, const char *aFormat, ...);
+
+/**
  * @}
  *
  */
diff --git a/include/openthread/message.h b/include/openthread/message.h
index 0b1430e..c39d99d 100644
--- a/include/openthread/message.h
+++ b/include/openthread/message.h
@@ -59,34 +59,6 @@
 typedef struct otMessage otMessage;
 
 /**
- * This structure represents the message buffer information.
- *
- */
-typedef struct otBufferInfo
-{
-    uint16_t mTotalBuffers;            ///< The number of buffers in the pool.
-    uint16_t mFreeBuffers;             ///< The number of free message buffers.
-    uint16_t m6loSendMessages;         ///< The number of messages in the 6lo send queue.
-    uint16_t m6loSendBuffers;          ///< The number of buffers in the 6lo send queue.
-    uint16_t m6loReassemblyMessages;   ///< The number of messages in the 6LoWPAN reassembly queue.
-    uint16_t m6loReassemblyBuffers;    ///< The number of buffers in the 6LoWPAN reassembly queue.
-    uint16_t mIp6Messages;             ///< The number of messages in the IPv6 send queue.
-    uint16_t mIp6Buffers;              ///< The number of buffers in the IPv6 send queue.
-    uint16_t mMplMessages;             ///< The number of messages in the MPL send queue.
-    uint16_t mMplBuffers;              ///< The number of buffers in the MPL send queue.
-    uint16_t mMleMessages;             ///< The number of messages in the MLE send queue.
-    uint16_t mMleBuffers;              ///< The number of buffers in the MLE send queue.
-    uint16_t mArpMessages;             ///< The number of messages in the ARP send queue.
-    uint16_t mArpBuffers;              ///< The number of buffers in the ARP send queue.
-    uint16_t mCoapMessages;            ///< The number of messages in the CoAP send queue.
-    uint16_t mCoapBuffers;             ///< The number of buffers in the CoAP send queue.
-    uint16_t mCoapSecureMessages;      ///< The number of messages in the CoAP secure send queue.
-    uint16_t mCoapSecureBuffers;       ///< The number of buffers in the CoAP secure send queue.
-    uint16_t mApplicationCoapMessages; ///< The number of messages in the application CoAP send queue.
-    uint16_t mApplicationCoapBuffers;  ///< The number of buffers in the application CoAP send queue.
-} otBufferInfo;
-
-/**
  * This enumeration defines the OpenThread message priority levels.
  *
  */
@@ -299,6 +271,35 @@
 } otMessageQueue;
 
 /**
+ * This structure represents information about a message queue.
+ *
+ */
+typedef struct otMessageQueueInfo
+{
+    uint16_t mNumMessages; ///< Number of messages in the queue.
+    uint16_t mNumBuffers;  ///< Number of data buffers used by messages in the queue.
+    uint32_t mTotalBytes;  ///< Total number of bytes used by all messages in the queue.
+} otMessageQueueInfo;
+
+/**
+ * This structure represents the message buffer information for different queues used by OpenThread stack.
+ *
+ */
+typedef struct otBufferInfo
+{
+    uint16_t           mTotalBuffers;         ///< The total number of buffers in the messages pool.
+    uint16_t           mFreeBuffers;          ///< The number of free buffers.
+    otMessageQueueInfo m6loSendQueue;         ///< Info about 6LoWPAN send queue.
+    otMessageQueueInfo m6loReassemblyQueue;   ///< Info about 6LoWPAN reassembly queue.
+    otMessageQueueInfo mIp6Queue;             ///< Info about IPv6 send queue.
+    otMessageQueueInfo mMplQueue;             ///< Info about MPL send queue.
+    otMessageQueueInfo mMleQueue;             ///< Info about MLE delayed message queue.
+    otMessageQueueInfo mCoapQueue;            ///< Info about CoAP/TMF send queue.
+    otMessageQueueInfo mCoapSecureQueue;      ///< Info about CoAP secure send queue.
+    otMessageQueueInfo mApplicationCoapQueue; ///< Info about application CoAP send queue.
+} otBufferInfo;
+
+/**
  * Initialize the message queue.
  *
  * This function MUST be called once and only once for a `otMessageQueue` instance before any other `otMessageQueue`
diff --git a/include/openthread/netdata.h b/include/openthread/netdata.h
index f67ea9b..74b92e8 100644
--- a/include/openthread/netdata.h
+++ b/include/openthread/netdata.h
@@ -127,11 +127,11 @@
 /**
  * This method provides a full or stable copy of the Partition's Thread Network Data.
  *
- * @param[in]     aInstance    A pointer to an OpenThread instance.
- * @param[in]     aStable      TRUE when copying the stable version, FALSE when copying the full version.
- * @param[out]    aData        A pointer to the data buffer.
- * @param[inout]  aDataLength  On entry, size of the data buffer pointed to by @p aData.
- *                             On exit, number of copied bytes.
+ * @param[in]      aInstance    A pointer to an OpenThread instance.
+ * @param[in]      aStable      TRUE when copying the stable version, FALSE when copying the full version.
+ * @param[out]     aData        A pointer to the data buffer.
+ * @param[in,out]  aDataLength  On entry, size of the data buffer pointed to by @p aData.
+ *                              On exit, number of copied bytes.
  *
  */
 otError otNetDataGet(otInstance *aInstance, bool aStable, uint8_t *aData, uint8_t *aDataLength);
@@ -139,10 +139,10 @@
 /**
  * This function gets the next On Mesh Prefix in the partition's Network Data.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the Network Data iterator context. To get the first on-mesh entry
-                             it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
- * @param[out]    aConfig    A pointer to where the On Mesh Prefix information will be placed.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the Network Data iterator context. To get the first on-mesh entry
+                              it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
+ * @param[out]     aConfig    A pointer to where the On Mesh Prefix information will be placed.
  *
  * @retval OT_ERROR_NONE       Successfully found the next On Mesh prefix.
  * @retval OT_ERROR_NOT_FOUND  No subsequent On Mesh prefix exists in the Thread Network Data.
@@ -155,10 +155,10 @@
 /**
  * This function gets the next external route in the partition's Network Data.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the Network Data iterator context. To get the first external route entry
-                             it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
- * @param[out]    aConfig    A pointer to where the External Route information will be placed.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the Network Data iterator context. To get the first external route entry
+                              it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
+ * @param[out]     aConfig    A pointer to where the External Route information will be placed.
  *
  * @retval OT_ERROR_NONE       Successfully found the next External Route.
  * @retval OT_ERROR_NOT_FOUND  No subsequent external route entry exists in the Thread Network Data.
@@ -169,10 +169,10 @@
 /**
  * This function gets the next service in the partition's Network Data.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the Network Data iterator context. To get the first service entry
-                             it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
- * @param[out]    aConfig    A pointer to where the service information will be placed.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the Network Data iterator context. To get the first service entry
+                              it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
+ * @param[out]     aConfig    A pointer to where the service information will be placed.
  *
  * @retval OT_ERROR_NONE       Successfully found the next service.
  * @retval OT_ERROR_NOT_FOUND  No subsequent service exists in the partition's Network Data.
@@ -231,6 +231,20 @@
                                                       const struct otJoinerDiscerner *aDiscerner);
 
 /**
+ * This function checks whether a given Prefix can act as a valid OMR prefix and also the Leader's Network Data contains
+ * this prefix.
+ *
+ * @param[in]  aInstance  A pointer to an OpenThread instance.
+ * @param[in]  aPrefix    A pointer to the IPv6 prefix.
+ *
+ * @returns  Whether @p aPrefix is a valid OMR prefix and Leader's Network Data contains the OMR prefix @p aPrefix.
+ *
+ * @note This API is only available when `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` is used.
+ *
+ */
+bool otNetDataContainsOmrPrefix(otInstance *aInstance, const otIp6Prefix *aPrefix);
+
+/**
  * @}
  *
  */
diff --git a/include/openthread/netdiag.h b/include/openthread/netdiag.h
index 2d291a0..d402383 100644
--- a/include/openthread/netdiag.h
+++ b/include/openthread/netdiag.h
@@ -264,10 +264,10 @@
 /**
  * This function gets the next Network Diagnostic TLV in the message.
  *
- * @param[in]     aMessage         A pointer to a message.
- * @param[inout]  aIterator        A pointer to the Network Diagnostic iterator context. To get the first
- *                                 Network Diagnostic TLV it should be set to OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT.
- * @param[out]    aNetworkDiagTlv  A pointer to where the Network Diagnostic TLV information will be placed.
+ * @param[in]      aMessage         A pointer to a message.
+ * @param[in,out]  aIterator        A pointer to the Network Diagnostic iterator context. To get the first
+ *                                  Network Diagnostic TLV it should be set to OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT.
+ * @param[out]     aNetworkDiagTlv  A pointer to where the Network Diagnostic TLV information will be placed.
  *
  * @retval OT_ERROR_NONE       Successfully found the next Network Diagnostic TLV.
  * @retval OT_ERROR_NOT_FOUND  No subsequent Network Diagnostic TLV exists in the message.
diff --git a/include/openthread/network_time.h b/include/openthread/network_time.h
index 23ab7e2..5e77231 100644
--- a/include/openthread/network_time.h
+++ b/include/openthread/network_time.h
@@ -78,8 +78,8 @@
 /**
  * Get the Thread network time.
  *
- * @param[in]    aInstance     The OpenThread instance structure.
- * @param[inout] aNetworkTime  The Thread network time in microseconds.
+ * @param[in]     aInstance     The OpenThread instance structure.
+ * @param[in,out] aNetworkTime  The Thread network time in microseconds.
  *
  * @returns The time synchronization status.
  *
diff --git a/include/openthread/platform/crypto.h b/include/openthread/platform/crypto.h
index 0d9bf5c..7cadc8e 100644
--- a/include/openthread/platform/crypto.h
+++ b/include/openthread/platform/crypto.h
@@ -139,13 +139,13 @@
 /**
  * Import a key into PSA ITS.
  *
- * @param[inout] aKeyRef           Pointer to the key ref to be used for crypto operations.
- * @param[in]    aKeyType          Key Type encoding for the key.
- * @param[in]    aKeyAlgorithm     Key algorithm encoding for the key.
- * @param[in]    aKeyUsage         Key Usage encoding for the key (combinations of `OT_CRYPTO_KEY_USAGE_*`).
- * @param[in]    aKeyPersistence   Key Persistence for this key
- * @param[in]    aKey              Actual key to be imported.
- * @param[in]    aKeyLen           Length of the key to be imported.
+ * @param[in,out] aKeyRef           Pointer to the key ref to be used for crypto operations.
+ * @param[in]     aKeyType          Key Type encoding for the key.
+ * @param[in]     aKeyAlgorithm     Key algorithm encoding for the key.
+ * @param[in]     aKeyUsage         Key Usage encoding for the key (combinations of `OT_CRYPTO_KEY_USAGE_*`).
+ * @param[in]     aKeyPersistence   Key Persistence for this key
+ * @param[in]     aKey              Actual key to be imported.
+ * @param[in]     aKeyLen           Length of the key to be imported.
  *
  * @retval OT_ERROR_NONE          Successfully imported the key.
  * @retval OT_ERROR_FAILED        Failed to import the key.
@@ -374,7 +374,7 @@
  *
  * @param[in]  aContext           Operation context for HKDF operation.
  * @param[in]  aSalt              Pointer to the Salt for HKDF.
- * @param[in]  aInfoLength        length of Salt.
+ * @param[in]  aSaltLength        Length of Salt.
  * @param[in]  aInputKey          Pointer to the input key.
  *
  * @retval OT_ERROR_NONE          HKDF Extract was successful.
@@ -455,7 +455,6 @@
  * Finish SHA-256 operation.
  *
  * @param[in]  aContext           Context for SHA-256 operation.
- * @param[in]  aContextSize       Context size SHA-256 operation.
  * @param[in]  aHash              A pointer to the output buffer, where hash needs to be stored.
  * @param[in]  aHashSize          The length of @p aHash in bytes.
  *
diff --git a/include/openthread/platform/logging.h b/include/openthread/platform/logging.h
index 4c62744..1efe361 100644
--- a/include/openthread/platform/logging.h
+++ b/include/openthread/platform/logging.h
@@ -115,6 +115,10 @@
 /**
  * This enumeration represents log regions.
  *
+ * The support for log region is removed and instead each core module can define its own name to appended to the logs.
+ * However, the `otLogRegion` enumeration is still defined as before to help with platforms which we may be using it
+ * in their `otPlatLog()` implementation. The OT core will always emit all logs with `OT_LOG_REGION_CORE`.
+ *
  */
 typedef enum otLogRegion
 {
@@ -146,6 +150,9 @@
 /**
  * This function outputs logs.
  *
+ * Note that the support for log region is removed. The OT core will always emit all logs with `OT_LOG_REGION_CORE`
+ * as @p aLogRegion.
+ *
  * @param[in]  aLogLevel   The log level.
  * @param[in]  aLogRegion  The log region.
  * @param[in]  aFormat     A pointer to the format string.
@@ -168,6 +175,19 @@
 void otPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine);
 
 /**
+ * This function handles OpenThread log level changes.
+ *
+ * This platform function is called whenever the OpenThread log level changes.
+ * This platform function is optional since an empty weak implementation has been provided.
+ *
+ * @note Only applicable when `OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE=1`.
+ *
+ * @param[in]  aLogLevel  The new OpenThread log level.
+ *
+ */
+void otPlatLogHandleLevelChanged(otLogLevel aLogLevel);
+
+/**
  * @}
  *
  */
diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h
index 2d7a654..6e85030 100644
--- a/include/openthread/platform/radio.h
+++ b/include/openthread/platform/radio.h
@@ -1126,8 +1126,8 @@
  * @param[in]  aInstance     The OpenThread instance structure.
  * @param[in]  aLinkMetrics  This parameter specifies what metrics to query. Per spec 4.11.3.4.4.6, at most 2 metrics
  *                           can be specified. The probing would be disabled if @p `aLinkMetrics` is bitwise 0.
- * @param[in]  aShortAddr    The short address of the Probing Initiator.
- * @param[in]  aExtAddr      The extended source address of the Probing Initiator. @p aExtAddr MUST NOT be `NULL`.
+ * @param[in]  aShortAddress The short address of the Probing Initiator.
+ * @param[in]  aExtAddress   The extended source address of the Probing Initiator. @p aExtAddr MUST NOT be `NULL`.
  *
  * @retval  OT_ERROR_NONE            Successfully configured the Enhanced-ACK Based Probing.
  * @retval  OT_ERROR_INVALID_ARGS    @p aExtAddress is `NULL`.
diff --git a/include/openthread/platform/settings.h b/include/openthread/platform/settings.h
index 39e0613..a5d6099 100644
--- a/include/openthread/platform/settings.h
+++ b/include/openthread/platform/settings.h
@@ -55,7 +55,7 @@
  * This enumeration defines the keys of settings.
  *
  * Note: When adding a new settings key, if the settings corresponding to the key contains security sensitive
- *       information, the developer MUST add the key to the array `kCriticalKeys`.
+ *       information, the developer MUST add the key to the array `kSensitiveKeys`.
  *
  */
 enum
@@ -68,21 +68,33 @@
     OT_SETTINGS_KEY_RESERVED             = 0x0006, ///< Reserved (previously auto-start).
     OT_SETTINGS_KEY_SLAAC_IID_SECRET_KEY = 0x0007, ///< SLAAC key to generate semantically opaque IID.
     OT_SETTINGS_KEY_DAD_INFO             = 0x0008, ///< Duplicate Address Detection (DAD) information.
-    OT_SETTINGS_KEY_OMR_PREFIX           = 0x0009, ///< Off-mesh routable (OMR) prefix.
+    OT_SETTINGS_KEY_LEGACY_OMR_PREFIX    = 0x0009, ///< Reserved. Legacy Off-mesh routable (OMR) prefix.
     OT_SETTINGS_KEY_ON_LINK_PREFIX       = 0x000a, ///< On-link prefix for infrastructure link.
     OT_SETTINGS_KEY_SRP_ECDSA_KEY        = 0x000b, ///< SRP client ECDSA public/private key pair.
     OT_SETTINGS_KEY_SRP_CLIENT_INFO      = 0x000c, ///< The SRP client info (selected SRP server address).
     OT_SETTINGS_KEY_SRP_SERVER_INFO      = 0x000d, ///< The SRP server info (UDP port).
-    OT_SETTINGS_KEY_NAT64_PREFIX         = 0x000e, ///< NAT64 prefix.
+    OT_SETTINGS_KEY_LEGACY_NAT64_PREFIX  = 0x000e, ///< Reserved. Legacy NAT64 prefix.
+    OT_SETTINGS_KEY_BR_ULA_PREFIX        = 0x000f, ///< BR ULA prefix.
+
+    // Keys in range 0x8000-0xffff are reserved for vendor-specific use.
+    OT_SETTINGS_KEY_VENDOR_RESERVED_MIN = 0x8000,
+    OT_SETTINGS_KEY_VENDOR_RESERVED_MAX = 0xffff,
 };
 
 /**
  * Performs any initialization for the settings subsystem, if necessary.
  *
- * @param[in]  aInstance The OpenThread instance structure.
+ * This function also sets the sensitive keys that should be stored in the secure area.
+ *
+ * Note that the memory pointed by @p aSensitiveKeys MUST not be released before @p aInstance is destroyed.
+ *
+ * @param[in]  aInstance             The OpenThread instance structure.
+ * @param[in]  aSensitiveKeys        A pointer to an array containing the list of sensitive keys. May be NULL only if
+ *                                   @p aSensitiveKeysLength is 0, which means that there is no sensitive keys.
+ * @param[in]  aSensitiveKeysLength  The number of entries in the @p aSensitiveKeys array.
  *
  */
-void otPlatSettingsInit(otInstance *aInstance);
+void otPlatSettingsInit(otInstance *aInstance, const uint16_t *aSensitiveKeys, uint16_t aSensitiveKeysLength);
 
 /**
  * Performs any de-initialization for the settings subsystem, if necessary.
@@ -92,18 +104,6 @@
  */
 void otPlatSettingsDeinit(otInstance *aInstance);
 
-/**
- * This function sets the critical keys that should be stored in the secure area.
- *
- * Note that the memory pointed by @p aKeys MUST not be released before @p aInstance is destroyed.
- *
- * @param[in]  aInstance    The OpenThread instance structure.
- * @param[in]  aKeys        A pointer to an array containing the list of critical keys.
- * @param[in]  aKeysLength  The number of entries in the @p aKeys array.
- *
- */
-void otPlatSettingsSetCriticalKeys(otInstance *aInstance, const uint16_t *aKeys, uint16_t aKeysLength);
-
 /// Fetches the value of a setting
 /** This function fetches the value of the setting identified
  *  by aKey and write it to the memory pointed to by aValue.
@@ -122,15 +122,15 @@
  *  values. The order of such values MAY change after ANY
  *  write operation to the store.
  *
- *  @param[in]     aInstance     The OpenThread instance structure.
- *  @param[in]     aKey          The key associated with the requested setting.
- *  @param[in]     aIndex        The index of the specific item to get.
- *  @param[out]    aValue        A pointer to where the value of the setting should be written. May be set to NULL if
- *                               just testing for the presence or length of a setting.
- *  @param[inout]  aValueLength  A pointer to the length of the value. When called, this pointer should point to an
- *                               integer containing the maximum value size that can be written to aValue. At return,
- *                               the actual length of the setting is written. This may be set to NULL if performing
- *                               a presence check.
+ *  @param[in]      aInstance     The OpenThread instance structure.
+ *  @param[in]      aKey          The key associated with the requested setting.
+ *  @param[in]      aIndex        The index of the specific item to get.
+ *  @param[out]     aValue        A pointer to where the value of the setting should be written. May be set to NULL if
+ *                                just testing for the presence or length of a setting.
+ *  @param[in,out]  aValueLength  A pointer to the length of the value. When called, this pointer should point to an
+ *                                integer containing the maximum value size that can be written to aValue. At return,
+ *                                the actual length of the setting is written. This may be set to NULL if performing
+ *                                a presence check.
  *
  *  @retval OT_ERROR_NONE             The given setting was found and fetched successfully.
  *  @retval OT_ERROR_NOT_FOUND        The given setting was not found in the setting store.
diff --git a/include/openthread/server.h b/include/openthread/server.h
index 05f3205..0d177c7 100644
--- a/include/openthread/server.h
+++ b/include/openthread/server.h
@@ -54,11 +54,11 @@
 /**
  * This method provides a full or stable copy of the local Thread Network Data.
  *
- * @param[in]     aInstance    A pointer to an OpenThread instance.
- * @param[in]     aStable      TRUE when copying the stable version, FALSE when copying the full version.
- * @param[out]    aData        A pointer to the data buffer.
- * @param[inout]  aDataLength  On entry, size of the data buffer pointed to by @p aData.
- *                             On exit, number of copied bytes.
+ * @param[in]      aInstance    A pointer to an OpenThread instance.
+ * @param[in]      aStable      TRUE when copying the stable version, FALSE when copying the full version.
+ * @param[out]     aData        A pointer to the data buffer.
+ * @param[in,out]  aDataLength  On entry, size of the data buffer pointed to by @p aData.
+ *                              On exit, number of copied bytes.
  *
  */
 otError otServerGetNetDataLocal(otInstance *aInstance, bool aStable, uint8_t *aData, uint8_t *aDataLength);
@@ -102,10 +102,10 @@
 /**
  * This function gets the next service in the local Network Data.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the Network Data iterator context. To get the first service entry
-                             it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
- * @param[out]    aConfig    A pointer to where the service information will be placed.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the Network Data iterator context. To get the first service entry
+                              it should be set to OT_NETWORK_DATA_ITERATOR_INIT.
+ * @param[out]     aConfig    A pointer to where the service information will be placed.
  *
  * @retval OT_ERROR_NONE       Successfully found the next service.
  * @retval OT_ERROR_NOT_FOUND  No subsequent service exists in the Thread Network Data.
diff --git a/include/openthread/srp_client.h b/include/openthread/srp_client.h
index ba7801e..72c0361 100644
--- a/include/openthread/srp_client.h
+++ b/include/openthread/srp_client.h
@@ -160,7 +160,7 @@
  *
  * @param[in] aError            The error (see above).
  * @param[in] aHostInfo         A pointer to host info.
- * @param[in] aService          The head of linked-list containing all services (excluding the ones removed). NULL if
+ * @param[in] aServices         The head of linked-list containing all services (excluding the ones removed). NULL if
  *                              the list is empty.
  * @param[in] aRemovedServices  The head of linked-list containing all removed services. NULL if the list is empty.
  * @param[in] aContext          A pointer to an arbitrary context (provided when callback was registered).
@@ -180,9 +180,9 @@
  * This callback is invoked when auto-start mode is enabled and the SRP client is either automatically started or
  * stopped.
  *
- * @param[in] aServerSockAddress   A non-NULL pointer indicates SRP server was started and pointer will give the
- *                                 selected server socket address. A NULL pointer indicates SRP server was stopped.
- * @param[in] aContext             A pointer to an arbitrary context (provided when callback was registered).
+ * @param[in] aServerSockAddr   A non-NULL pointer indicates SRP server was started and pointer will give the
+ *                              selected server socket address. A NULL pointer indicates SRP server was stopped.
+ * @param[in] aContext          A pointer to an arbitrary context (provided when callback was registered).
  *
  */
 typedef void (*otSrpClientAutoStartCallback)(const otSockAddr *aServerSockAddr, void *aContext);
@@ -408,15 +408,15 @@
  * request from an earlier call to `otSrpClientRemoveHostAndServices()` and host info still being in  either
  * `STATE_TO_REMOVE` or `STATE_REMOVING` states).
  *
- * The host IPv6 address array pointed to by @p aAddresses MUST persist and remain unchanged after returning from this
- * function (with `OT_ERROR_NONE`). OpenThread will save the pointer to the array.
+ * The host IPv6 address array pointed to by @p aIp6Addresses MUST persist and remain unchanged after returning from
+ * this function (with `OT_ERROR_NONE`). OpenThread will save the pointer to the array.
  *
  * After a successful call to this function, `otSrpClientCallback` will be called to report the status of the address
  * registration with SRP server.
  *
  * @param[in] aInstance           A pointer to the OpenThread instance.
- * @param[in] aAddresses          A pointer to the an array containing the host IPv6 addresses.
- * @param[in] aNumAddresses       The number of addresses in the @p aAddresses array.
+ * @param[in] aIp6Addresses       A pointer to the an array containing the host IPv6 addresses.
+ * @param[in] aNumAddresses       The number of addresses in the @p aIp6Addresses array.
  *
  * @retval OT_ERROR_NONE            The host IPv6 address list change started successfully. The `otSrpClientCallback`
  *                                  will be called to report the status of registering addresses with server.
diff --git a/include/openthread/tcp.h b/include/openthread/tcp.h
index 46f12c4..6cc11f7 100644
--- a/include/openthread/tcp.h
+++ b/include/openthread/tcp.h
@@ -29,7 +29,7 @@
 /**
  * @file
  * @brief
- *  This file defines the OpenThread TCP API.
+ *   This file defines the OpenThread TCP API.
  *
  */
 
@@ -64,7 +64,7 @@
 {
     struct otLinkedBuffer *mNext;   ///< Pointer to the next linked buffer in the chain, or NULL if it is the end.
     const uint8_t *        mData;   ///< Pointer to data referenced by this linked buffer.
-    uint16_t               mLength; ///< Length of this linked buffer (number of bytes).
+    size_t                 mLength; ///< Length of this linked buffer (number of bytes).
 } otLinkedBuffer;
 
 struct otTcpEndpoint;
@@ -95,47 +95,74 @@
 typedef void (*otTcpSendDone)(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData);
 
 /**
- * This callback informs the application that the first @p aNumBytes in the
- * send buffer have been acknowledged by the connection peer and that their
- * underlying memory can be reclaimed by the application.
+ * This callback informs the application if forward progress has been made in
+ * transferring data from the send buffer to the recipient. This callback is
+ * not necessary for correct TCP operation. Most applications can just rely on
+ * the otTcpSendDone() callback to reclaim linked buffers once the TCP stack is
+ * done using them. The purpose of this callback is to support advanced
+ * applications that benefit from finer-grained information about how the
+ * the connection is making forward progress in transferring data to the
+ * connection peer.
  *
- * This callback is not necessary for correct TCP operation. Most applications
- * can just rely on the otTcpSendDone() callback. If an application wants
- * fine-grained feedback as memory in the send buffer becomes available again
- * (instead of waiting for an entire linked buffer's worth of data becomes
- * available) or some indication as to whether the connection is making forward
- * progress, it can register this callback.
+ * This callback's operation is closely tied to TCP's send buffer. The send
+ * buffer can be understood as having two regions. First, there is the
+ * "in-flight" region at the head (front) of the send buffer. It corresponds
+ * to data which has been sent to the recipient, but is not yet acknowledged.
+ * Second, there is the "backlog" region, which consists of all data in the
+ * send buffer that is not in the "in-flight" region. The "backlog" region
+ * corresponds to data that is queued for sending, but has not yet been sent.
  *
- * @param[in]  aEndpoint  The TCP endpoint for the connection.
- * @param[in]  aNumBytes  The number of bytes newly acknowledged by the connection peer.
+ * The callback is invoked in response to two types of events. First, the
+ * "in-flight" region of the send buffer may shrink (e.g., when the recipient
+ * acknowledges data that we sent earlier). Second, the "backlog" region of the
+ * send buffer may shrink (e.g., new data was sent out). These two conditions
+ * often occur at the same time, in response to an ACK segment from the
+ * connection peer, which is why they are combined in a single callback.
+ *
+ * The TCP stack only uses the @p aInSendBuffer bytes at the tail of the send
+ * buffer; when @p aInSendBuffer decreases by an amount x, it means that x
+ * additional bytes that were formerly at the head of the send buffer are no
+ * longer part of the send buffer and can now be reclaimed (i.e., overwritten)
+ * by the application. Note that the otLinkedBuffer structure itself can only
+ * be reclaimed once all bytes that it references are no longer part of the
+ * send buffer.
+ *
+ * This callback subsumes otTcpSendDone(), in the following sense: applications
+ * can determine when linked buffers can be reclaimed by comparing
+ * @p aInSendBuffer with how many bytes are in each linked buffer. However, we
+ * expect otTcpSendDone(), which directly conveys which otLinkedBuffers can be
+ * reclaimed, to be much simpler to use. If both callbacks are registered and
+ * are triggered by the same event (e.g., the same ACK segment received), then
+ * the otTcpSendDone() callback will be triggered first, followed by this
+ * callback.
+ *
+ * Additionally, this callback provides @p aBacklog, which indicates how many
+ * bytes of data in the send buffer are not yet in flight. For applications
+ * that only want to add data to the send buffer when there is an assurance
+ * that it will be sent out soon, it may be desirable to only send out data
+ * when @p aBacklog is suitably small (0 or close to 0). For example, an
+ * application may use @p aBacklog so that it can react to queue buildup by
+ * dropping or aggregating data to avoid creating a backlog of data.
+ *
+ * After a call to otTcpSendByReference() or otTcpSendByExtension() with a
+ * positive number of bytes, the otTcpForwardProgress() callback is guaranteed
+ * to be called, to indicate when the bytes that were added to the send buffer
+ * are sent out. The call to otTcpForwardProgress() may be made immediately
+ * after the bytes are added to the send buffer (if some of those bytes are
+ * immediately sent out, reducing the backlog), or sometime in the future (once
+ * the connection sends out some or all of the data, reducing the backlog). By
+ * "immediately," we mean that the callback is immediately scheduled for
+ * execution in a tasklet; to avoid reentrancy-related complexity, the
+ * otTcpForwardProgress() callback is never directly called from the
+ * otTcpSendByReference() or otTcpSendByExtension() functions.
+ *
+ * @param[in]  aEndpoint      The TCP endpoint for the connection.
+ * @param[in]  aInSendBuffer  The number of bytes in the send buffer (sum of "in-flight" and "backlog" regions).
+ * @param[in]  aBacklog       The number of bytes that are queued for sending but have not yet been sent (the "backlog"
+ *                            region).
  *
  */
-typedef void (*otTcpBytesAcked)(otTcpEndpoint *aEndpoint, size_t aNumBytes);
-
-/**
- * This callback informs the application that if data is added to the send
- * buffer, some of it will be transmitted immediately without delay, as opposed
- * to being queued for transmission once the peer ACKs some data.
- *
- * After a call to otTcpSendByReference() or otTcpSendByExtension(), the
- * otTcpSendReady() callback is guaranteed to be called, either immediately (if
- * the connection is already ready) or sometime in the future (once the
- * connection becomes ready for more data).
- *
- * This callback is not necessary for correct TCP operation. If more data is
- * added to the send buffer than can be transmitted without delay, it will
- * simply be queued for transmission at a later time. This callback should be
- * used only in cases where some assurance is desired that data added to the
- * send buffer will be sent soon (e.g., TCP won't wait for the recipient to
- * ACK some other data first before sending this data out). For example, you
- * may use this callback if you'd rather have your data be dropped than develop
- * a backlog of data in your send buffer. But for most applications, where this
- * isn't a concern, it's expected that one would not use this callback at all.
- *
- * @param[in]  aEndpoint  The TCP endpoint for the connection.
- *
- */
-typedef void (*otTcpSendReady)(otTcpEndpoint *aEndpoint);
+typedef void (*otTcpForwardProgress)(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog);
 
 /**
  * This callback indicates the number of bytes available for consumption from
@@ -226,7 +253,7 @@
 
     otTcpEstablished      mEstablishedCallback;      ///< "Established" callback function
     otTcpSendDone         mSendDoneCallback;         ///< "Send done" callback function
-    otTcpSendReady        mSendReadyCallback;        ///< "Send ready" callback function
+    otTcpForwardProgress  mForwardProgressCallback;  ///< "Forward progress" callback function
     otTcpReceiveAvailable mReceiveAvailableCallback; ///< "Receive available" callback function
     otTcpDisconnected     mDisconnectedCallback;     ///< "Disconnected" callback function
 
@@ -234,6 +261,8 @@
 
     otLinkedBuffer mReceiveLinks[2];
     otSockAddr     mSockAddr;
+
+    uint8_t mPendingCallbacks;
 };
 
 /**
@@ -246,8 +275,7 @@
 
     otTcpEstablished      mEstablishedCallback;      ///< "Established" callback function
     otTcpSendDone         mSendDoneCallback;         ///< "Send done" callback function
-    otTcpBytesAcked       mBytesAckedCallback;       ///< "Bytes acked" callback
-    otTcpSendReady        mSendReadyCallback;        ///< "Send ready" callback function
+    otTcpForwardProgress  mForwardProgressCallback;  ///< "Forward progress" callback function
     otTcpReceiveAvailable mReceiveAvailableCallback; ///< "Receive available" callback function
     otTcpDisconnected     mDisconnectedCallback;     ///< "Disconnected" callback function
 
@@ -297,7 +325,9 @@
  * @retval OT_ERROR_FAILED  Failed to open the TCP endpoint.
  *
  */
-otError otTcpEndpointInitialize(otInstance *aInstance, otTcpEndpoint *aEndpoint, otTcpEndpointInitializeArgs *aArgs);
+otError otTcpEndpointInitialize(otInstance *                       aInstance,
+                                otTcpEndpoint *                    aEndpoint,
+                                const otTcpEndpointInitializeArgs *aArgs);
 
 /**
  * Obtains the otInstance that was associated with @p aEndpoint upon
@@ -514,9 +544,10 @@
  *
  * This immediately makes the TCP endpoint free for use for another connection
  * and empties the send and receive buffers, transferring ownership of any data
- * provided by the application in otTcpSendByReference() calls back to
- * the application. The TCP endpoint's callbacks and memory for the receive
- * buffer remain associated with the TCP endpoint.
+ * provided by the application in otTcpSendByReference() and
+ * otTcpSendByExtension() calls back to the application. The TCP endpoint's
+ * callbacks and memory for the receive buffer remain associated with the
+ * TCP endpoint.
  *
  * @param[in]  aEndpoint  A pointer to the TCP endpoint structure representing the TCP endpoint to abort.
  *
@@ -678,7 +709,9 @@
  * @retval OT_ERROR_FAILED  Failed to open the TCP listener.
  *
  */
-otError otTcpListenerInitialize(otInstance *aInstance, otTcpListener *aListener, otTcpListenerInitializeArgs *aArgs);
+otError otTcpListenerInitialize(otInstance *                       aInstance,
+                                otTcpListener *                    aListener,
+                                const otTcpListenerInitializeArgs *aArgs);
 
 /**
  * Obtains the otInstance that was associated with @p aListener upon
diff --git a/include/openthread/tcp_ext.h b/include/openthread/tcp_ext.h
new file mode 100644
index 0000000..5c0ddc6
--- /dev/null
+++ b/include/openthread/tcp_ext.h
@@ -0,0 +1,216 @@
+/*
+ *  Copyright (c) 2022, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief
+ *   This file defines extensions to the OpenThread TCP API.
+ *
+ */
+
+#ifndef OPENTHREAD_TCP_EXT_H_
+#define OPENTHREAD_TCP_EXT_H_
+
+#include <openthread/tcp.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @addtogroup api-tcp-ext
+ *
+ * @brief
+ *   This module includes easy-to-use abstractions on top of the base TCP API.
+ *
+ * @{
+ *
+ */
+
+/**
+ * This structure represents a circular send buffer for use with a TCP endpoint.
+ *
+ * Using a circular send buffer is optional. Applications can use a TCP
+ * endpoint to send data by managing otLinkedBuffers directly. However, some
+ * applications may find it more convenient to have a circular send buffer;
+ * such applications can call otTcpCircularSendBufferWrite() to "attach" a
+ * circular send buffer to a TCP endpoint and send out data on that TCP
+ * endpoint, relying on the circular send buffer to manage the underlying
+ * otLinkedBuffers.
+ *
+ * otTcpCircularSendBuffer is implemented on top of the otLinkedBuffer-based
+ * API provided by an otTcpEndpoint. Once attached to an otTcpEndpoint, an
+ * otTcpCircularSendBuffer performs all the work of managing otLinkedBuffers
+ * for the connection. This means that, once an otTcpCircularSendBuffer is
+ * attached to an otTcpEndpoint, the application should not call
+ * otTcpSendByReference() or otTcpSendByExtension() on that otTcpEndpoint.
+ * Instead, the application should use otTcpCircularSendBufferWrite() to add
+ * data to the send buffer.
+ *
+ * The otTcpForwardProgress() callback is the intended way for users to learn
+ * when space becomes available in the circular send buffer. On an
+ * otTcpEndpoint to which an otTcpCircularSendBuffer is attached, the
+ * application MUST install an otTcpForwardProgress() callback and call
+ * otTcpCircularSendBufferHandleForwardProgress() on the attached
+ * otTcpCircularSendBuffer at the start of the callback function. It is
+ * recommended that the user NOT install an otTcpSendDone() callback, as all
+ * management of otLinkedBuffers is handled by the circular send buffer.
+ *
+ * The application should not inspect the fields of this structure directly; it
+ * should only interact with it via the TCP Circular Send Buffer API functions
+ * whose signature are provided in this file.
+ *
+ */
+typedef struct otTcpCircularSendBuffer
+{
+    const uint8_t *mDataBuffer;   ///< Pointer to data in the circular send buffer
+    size_t         mCapacity;     ///< Length of the circular send buffer
+    size_t         mStartIndex;   ///< Index of the first valid byte in the send buffer
+    size_t         mCapacityUsed; ///< Number of bytes stored in the send buffer
+
+    otLinkedBuffer mSendLinks[2];
+    uint8_t        mFirstSendLinkIndex;
+} otTcpCircularSendBuffer;
+
+/**
+ * Initializes a TCP circular send buffer.
+ *
+ * @param[in]  aSendBuffer      A pointer to the TCP circular send buffer to initialize.
+ * @param[in]  aDataBuffer      A pointer to memory to use to store data in the TCP circular send buffer.
+ * @param[in]  aCapacity        The capacity, in bytes, of the TCP circular send buffer, which must equal the size of
+ *                              the memory pointed to by @p aDataBuffer .
+ */
+void otTcpCircularSendBufferInitialize(otTcpCircularSendBuffer *aSendBuffer, void *aDataBuffer, size_t aCapacity);
+
+/**
+ * Sends out data on a TCP endpoint, using the provided TCP circular send
+ * buffer to manage buffering.
+ *
+ * Once this function is called, @p aSendBuffer and @p aEndpoint are considered
+ * "attached" to each other. While they are attached, ALL send operations for
+ * @p aEndpoint must be made using @p aSendBuffer and ALL operations on
+ * @p aSendBuffer must be associated with @p aEndpoint .
+ *
+ * The only way to "detach" a TCP circular send buffer and a TCP endpoint is to
+ * wait for the send buffer to become completely empty. This can happen in two
+ * ways: (1) all data in the send buffer is sent and acknowledged in the normal
+ * course of TCP protocol operation, or (2) the connection is terminated.
+ *
+ * The recommended usage pattern is to use a single TCP circular send buffer
+ * with a TCP endpoint, and to send data on that TCP endpoint only via its
+ * associated TCP circular buffer. This recommended usage pattern sidesteps the
+ * issues described above by always using a TCP endpoint and TCP circular send
+ * buffer together.
+ *
+ * If the circular send buffer reaches capacity, only a prefix of the provided
+ * data is copied into the circular send buffer.
+ *
+ * @param[in]   aEndpoint    The TCP endpoint on which to send out data.
+ * @param[in]   aSendBuffer  The TCP circular send buffer into which to copy data.
+ * @param[in]   aData        A pointer to data to copy into the TCP circular send buffer.
+ * @param[in]   aLength      The length of the data pointed to by @p aData to copy into the TCP circular send buffer.
+ * @param[out]  aWritten     Populated with the amount of data copied into the send buffer, which might be less than
+ *                           @p aLength if the send buffer reaches capacity.
+ *
+ * @returns OT_ERROR_NONE    Successfully copied data into the send buffer and sent it on the TCP endpoint.
+ * @returns OT_ERROR_FAILED  Failed to send out data on the TCP endpoint.
+ */
+otError otTcpCircularSendBufferWrite(otTcpEndpoint *          aEndpoint,
+                                     otTcpCircularSendBuffer *aSendBuffer,
+                                     void *                   aData,
+                                     size_t                   aLength,
+                                     size_t *                 aWritten);
+
+/**
+ * Performs circular-send-buffer-specific handling in the otTcpForwardProgress
+ * callback.
+ *
+ * The application is expected to install an otTcpForwardProgress() callback on
+ * the otTcpEndpoint, and call this function at the start of the callback
+ * function for circular-send-buffer-specific processing.
+ *
+ * In the callback function, the application can determine the amount of free
+ * space in the circular send buffer by calling
+ * otTcpCircularSendBufferFreeSpace(), or by comparing @p aInSendBuffer with
+ * the send buffer's capacity, chosen by the user when calling
+ * otTcpCircularSendBufferInitialize().
+ *
+ * @param[in]  aSendBuffer    A pointer to the TCP circular send buffer for the endpoint for which
+ *                            otTcpForwardProgress() was invoked.
+ * @param[in]  aInSendBuffer  Value of @p aInSendBuffer passed to the otTcpForwardProgress() callback.
+ */
+void otTcpCircularSendBufferHandleForwardProgress(otTcpCircularSendBuffer *aSendBuffer, size_t aInSendBuffer);
+
+/**
+ * Returns the amount of free space in the TCP circular send buffer.
+ *
+ * This operation will always succeed.
+ *
+ * @param[in]  aSendBuffer  A pointer to the TCP circular send buffer whose amount of free space to return.
+ *
+ * @return The amount of free space in the send buffer.
+ */
+size_t otTcpCircularSendBufferFreeSpace(otTcpCircularSendBuffer *aSendBuffer);
+
+/**
+ * Forcibly discards all data in the circular send buffer.
+ *
+ * The application is expected to call this function when a TCP connection is
+ * terminated unceremoniously (e.g., if the application calls
+ * otTcpEndpointAbort() or is informed of a reset connection via the
+ * otTcpConnectionLost() callback).
+ *
+ * Calling this function on a nonempty TCP circular send buffer attached to a
+ * TCP endpoint results in undefined behavior.
+ *
+ * @param[in]  aSendBuffer  The TCP circular send buffer whose data to discard.
+ */
+void otTcpCircularSendBufferForceDiscardAll(otTcpCircularSendBuffer *aSendBuffer);
+
+/**
+ * Deinitializes a TCP circular send buffer, detaching it if attached.
+ *
+ * If the TCP circular send buffer is not empty, then this operation will fail.
+ *
+ * @param[in]  aSendBuffer  The TCP circular send buffer to deinitialize.
+ *
+ * @retval OT_ERROR_NONE    Successfully deinitialize the TCP circular send buffer.
+ * @retval OT_ERROR_BUSY    Circular buffer contains data and cannot be deinitialized.
+ */
+otError otTcpCircularSendBufferDeinitialize(otTcpCircularSendBuffer *aSendBuffer);
+
+/**
+ * @}
+ *
+ */
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // OPENTHREAD_TCP_EXT_H_
diff --git a/include/openthread/thread.h b/include/openthread/thread.h
index b23bd66..fe82900 100644
--- a/include/openthread/thread.h
+++ b/include/openthread/thread.h
@@ -714,10 +714,10 @@
  * This function gets the next neighbor information. It is used to go through the entries of
  * the neighbor table.
  *
- * @param[in]     aInstance  A pointer to an OpenThread instance.
- * @param[inout]  aIterator  A pointer to the iterator context. To get the first neighbor entry
-                             it should be set to OT_NEIGHBOR_INFO_ITERATOR_INIT.
- * @param[out]    aInfo      A pointer to the neighbor information.
+ * @param[in]      aInstance  A pointer to an OpenThread instance.
+ * @param[in,out]  aIterator  A pointer to the iterator context. To get the first neighbor entry
+                              it should be set to OT_NEIGHBOR_INFO_ITERATOR_INIT.
+ * @param[out]     aInfo      A pointer to the neighbor information.
  *
  * @retval OT_ERROR_NONE         Successfully found the next neighbor entry in table.
  * @retval OT_ERROR_NOT_FOUND     No subsequent neighbor entry exists in the table.
@@ -872,7 +872,7 @@
 /**
  * This function pointer is called every time an MLE Parent Response message is received.
  *
- * @param[in]  aStats    pointer to a location on stack holding the stats data.
+ * @param[in]  aInfo     A pointer to a location on stack holding the stats data.
  * @param[in]  aContext  A pointer to callback client-specific context.
  *
  */
diff --git a/include/openthread/thread_ftd.h b/include/openthread/thread_ftd.h
index c54ea74..ea052de 100644
--- a/include/openthread/thread_ftd.h
+++ b/include/openthread/thread_ftd.h
@@ -484,12 +484,12 @@
 /**
  * This function gets the next IPv6 address (using an iterator) for a given child.
  *
- * @param[in]     aInstance    A pointer to an OpenThread instance.
- * @param[in]     aChildIndex  The child index.
- * @param[inout]  aIterator    A pointer to the iterator. On success the iterator will be updated to point to next
- *                             entry in the list. To get the first IPv6 address the iterator should be set to
- *                             OT_CHILD_IP6_ADDRESS_ITERATOR_INIT.
- * @param[out]    aAddress     A pointer to an IPv6 address where the child's next address is placed (on success).
+ * @param[in]      aInstance    A pointer to an OpenThread instance.
+ * @param[in]      aChildIndex  The child index.
+ * @param[in,out]  aIterator    A pointer to the iterator. On success the iterator will be updated to point to next
+ *                              entry in the list. To get the first IPv6 address the iterator should be set to
+ *                              OT_CHILD_IP6_ADDRESS_ITERATOR_INIT.
+ * @param[out]     aAddress     A pointer to an IPv6 address where the child's next address is placed (on success).
  *
  * @retval OT_ERROR_NONE          Successfully found the next IPv6 address (@p aAddress was successfully updated).
  * @retval OT_ERROR_NOT_FOUND     The child has no subsequent IPv6 address entry.
@@ -540,11 +540,11 @@
 /**
  * This function gets the next EID cache entry (using an iterator).
  *
- * @param[in]    aInstance   A pointer to an OpenThread instance.
- * @param[out]   aEntryInfo  A pointer to where the EID cache entry information is placed.
- * @param[inout] aIterator   A pointer to an iterator. It will be updated to point to next entry on success. To get the
- *                           first entry, initialize the iterator by setting all its fields to zero (e.g., `memset` the
- *                           the iterator structure to zero).
+ * @param[in]     aInstance   A pointer to an OpenThread instance.
+ * @param[out]    aEntryInfo  A pointer to where the EID cache entry information is placed.
+ * @param[in,out] aIterator   A pointer to an iterator. It will be updated to point to next entry on success. To get
+ *                            the first entry, initialize the iterator by setting all its fields to zero
+ *                            (e.g., `memset` the iterator structure to zero).
  *
  * @retval OT_ERROR_NONE          Successfully populated @p aEntryInfo for next EID cache entry.
  * @retval OT_ERROR_NOT_FOUND     No more entries in the address cache table.
@@ -605,7 +605,7 @@
  * non-volatile memory.
  *
  * @param[in]  aInstance   A pointer to an OpenThread instance.
- * @param[in]  aPskcRef    Key Reference to the new Thread PSKc.
+ * @param[in]  aKeyRef     Key Reference to the new Thread PSKc.
  *
  * @retval OT_ERROR_NONE           Successfully set the Thread PSKc.
  * @retval OT_ERROR_INVALID_STATE  Thread protocols are enabled.
diff --git a/openthread_upstream_version.gni b/openthread_upstream_version.gni
index 9df7b4c..e340913 100644
--- a/openthread_upstream_version.gni
+++ b/openthread_upstream_version.gni
@@ -1,3 +1,3 @@
 # This file is added to support soft-transition in Fuchsia.
 
-openthread_upstream_version = "74f5ab5c9f434c4ffe44c580be40b7ff14e7f0e9"
+openthread_upstream_version = "645fbcd400d2e6c2de641bccb0335e3eaaed1f36"
diff --git a/script/check-android-build b/script/check-android-build
index cf20557..6647b9f 100755
--- a/script/check-android-build
+++ b/script/check-android-build
@@ -51,12 +51,12 @@
 
     datetime="$(date)"
     cat >openthread-config-datetime.h <<EOF
-#include <openthread-config-android.h>
 #define OPENTHREAD_BUILD_DATETIME "$datetime"
 EOF
 
     OPENTHREAD_PROJECT_CFLAGS="-I$PWD -DOPENTHREAD_CONFIG_FILE=\\\"openthread-config-datetime.h\\\" \
-        -DOPENTHREAD_PROJECT_CORE_CONFIG_FILE=\\\"openthread-core-posix-config.h\\\"" \
+        -DOPENTHREAD_PROJECT_CORE_CONFIG_FILE=\\\"openthread-core-posix-config.h\\\" \
+        -std=c99" \
         make showcommands ot-cli
     grep "$datetime" -ao "out/target/product/generic/system/bin/ot-cli"
     make clean-ot-cli
diff --git a/script/check-simulation-build-autotools b/script/check-simulation-build-autotools
index ac55070..6b16957 100755
--- a/script/check-simulation-build-autotools
+++ b/script/check-simulation-build-autotools
@@ -99,7 +99,7 @@
     )
 
     # Build Thread 1.1 with full features and no log
-    export CPPFLAGS="${options[*]} -DOPENTHREAD_CONFIG_LOG_LEVEL=OT_LOG_LEVEL_NONE"
+    export CPPFLAGS="${options[*]} -DOPENTHREAD_CONFIG_LOG_OUTPUT=OT_LOG_OUTPUT_NONE"
     reset_source
     make -f examples/Makefile-simulation THREAD_VERSION=1.1
 
@@ -109,7 +109,7 @@
     make -f examples/Makefile-simulation THREAD_VERSION=1.1 FULL_LOGS=1
 
     # Build Thread 1.2 with full features and logs
-    export CPPFLAGS="${options[*]} ${options_1_2[*]} -DOPENTHREAD_CONFIG_LOG_LEVEL=OT_LOG_LEVEL_NONE"
+    export CPPFLAGS="${options[*]} ${options_1_2[*]} -DOPENTHREAD_CONFIG_LOG_OUTPUT=OT_LOG_OUTPUT_NONE"
     reset_source
     make -f examples/Makefile-simulation THREAD_VERSION=1.2
 
diff --git a/script/check-simulation-build-cmake b/script/check-simulation-build-cmake
index 0975ed2..e9a3b45 100755
--- a/script/check-simulation-build-cmake
+++ b/script/check-simulation-build-cmake
@@ -89,6 +89,10 @@
     # Build Thread 1.2 with full features and OT_ASSERT=OFF
     reset_source
     "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_DUA=ON -DOT_ASSERT=OFF
+
+    # Build with RAM settings
+    reset_source
+    "$(dirname "$0")"/cmake-build simulation -DOT_SETTINGS_RAM=ON
 }
 
 build_toranj()
diff --git a/src/cli/README.md b/src/cli/README.md
index 1085b2b..1dd92e4 100644
--- a/src/cli/README.md
+++ b/src/cli/README.md
@@ -107,6 +107,7 @@
 - [sntp](#sntp-query-sntp-server-ip-sntp-server-port)
 - [state](#state)
 - [srp](README_SRP.md)
+- [tcp](README_TCP.md)
 - [thread](#thread-start)
 - [trel](#trel)
 - [tvcheck](#tvcheck-enable)
@@ -324,17 +325,6 @@
 Done
 ```
 
-### bbr skipseqnuminc
-
-Skip increase of Sequence Number when updating the local BBR Dataset from the Network Data.
-
-Only for testing/reference device.
-
-```bash
-> bbr skipseqnuminc
-Done
-```
-
 ### ba
 
 Show current Border Agent information.
@@ -409,19 +399,25 @@
 
 Show the current message buffer information.
 
+- The `total` shows total number of message buffers in pool.
+- The `free` shows the number of free message buffers.
+- This is then followed by info about different queues used by OpenThread stack, each line representing info about a queue.
+  - The first number shows number messages in the queue.
+  - The second number shows number of buffers used by all messages in the queue.
+  - The third number shows total number of bytes of all messages in the queue.
+
 ```bash
 > bufferinfo
 total: 40
 free: 40
-6lo send: 0 0
-6lo reas: 0 0
-ip6: 0 0
-mpl: 0 0
-mle: 0 0
-arp: 0 0
-coap: 0 0
-coap secure: 0 0
-application coap: 0 0
+6lo send: 0 0 0
+6lo reas: 0 0 0
+ip6: 0 0 0
+mpl: 0 0 0
+mle: 0 0 0
+coap: 0 0 0
+coap secure: 0 0 0
+application coap: 0 0 0
 Done
 ```
 
@@ -2181,6 +2177,15 @@
 Done
 ```
 
+### prefix meshlocal <prefix>
+
+Set the mesh local prefix.
+
+```bash
+> prefix meshlocal fdde:ad00:beef:0::/64
+Done
+```
+
 ### prefix remove \<prefix\>
 
 Invalidate a prefix in the Network Data.
@@ -2491,9 +2496,9 @@
 
 ```bash
 > scan
-| J | Network Name     | Extended PAN     | PAN  | MAC Address      | Ch | dBm | LQI |
-+---+------------------+------------------+------+------------------+----+-----+-----+
-| 0 | OpenThread       | dead00beef00cafe | ffff | f1d92a82c8d8fe43 | 11 | -20 |   0 |
+| PAN  | MAC Address      | Ch | dBm | LQI |
++------+------------------+----+-----+-----+
+| ffff | f1d92a82c8d8fe43 | 11 | -20 |   0 |
 Done
 ```
 
diff --git a/src/cli/README_COMMISSIONER.md b/src/cli/README_COMMISSIONER.md
index 7cd7079..013b416 100644
--- a/src/cli/README_COMMISSIONER.md
+++ b/src/cli/README_COMMISSIONER.md
@@ -11,6 +11,7 @@
 - [energy](#energy)
 - [joiner add](#joiner-add)
 - [joiner remove](#joiner-remove)
+- [joiner table](#joiner-table)
 - [mgmtget](#mgmtget)
 - [mgmtset](#mgmtset)
 - [panid](#panid)
@@ -120,6 +121,22 @@
 Done
 ```
 
+### joiner table
+
+Usage: `commissioner joiner table`
+
+List all Joiner entries.
+
+```bash
+> commissioner joiner table
+| ID                    | PSKd                             | Expiration |
++-----------------------+----------------------------------+------------+
+|                     * |                           J01NME |      81015 |
+|      d45e64fa83f81cf7 |                           J01NME |     101204 |
+| 0x0000000000000abc/12 |                           J01NME |     114360 |
+Done
+```
+
 ### mgmtget
 
 Usage: `commissioner mgmtget [locator] [sessionid] [steeringdata] [joinerudpport] [-x <TLV Types>]`
@@ -183,6 +200,27 @@
 Done
 ```
 
+### id
+
+Usage: `commissioner id`
+
+Get the commissioner id.
+
+```bash
+> commissioner id
+OpenThread Commissioner
+Done
+```
+
+### id \<name\>
+
+Set the commissioner id.
+
+```bash
+> commissioner id "Custom Commissioner Id"
+Done
+```
+
 ### start
 
 Usage: `commissioner start`
diff --git a/src/cli/README_COMMISSIONING.md b/src/cli/README_COMMISSIONING.md
index 237b65d..8262a8c 100644
--- a/src/cli/README_COMMISSIONING.md
+++ b/src/cli/README_COMMISSIONING.md
@@ -21,7 +21,7 @@
    Network Name: OpenThread-8f28
    PAN ID: 0x8f28
    PSKc: c23a76e98f1a6483639b1ac1271e2e27
-   Security Policy: 0, onrcb
+   Security Policy: 0, onrc
    Done
    ```
 
@@ -107,7 +107,7 @@
    Network Name: OpenThread-8f28
    PAN ID: 0x8f28
    PSKc: c23a76e98f1a6483639b1ac1271e2e27
-   Security Policy: 0, onrcb
+   Security Policy: 0, onrc
    Done
    ```
 
diff --git a/src/cli/README_DATASET.md b/src/cli/README_DATASET.md
index a9f678f..1043bba 100644
--- a/src/cli/README_DATASET.md
+++ b/src/cli/README_DATASET.md
@@ -44,7 +44,7 @@
    Network Name: OpenThread-8f28
    PAN ID: 0x8f28
    PSKc: c23a76e98f1a6483639b1ac1271e2e27
-   Security Policy: 0, onrcb
+   Security Policy: 0, onrc
    Done
    ```
 
@@ -103,7 +103,7 @@
    Network Name: OpenThread-8f28
    PAN ID: 0x8f28
    PSKc: c23a76e98f1a6483639b1ac1271e2e27
-   Security Policy: 0, onrcb
+   Security Policy: 0, onrc
    Done
    ```
 
@@ -180,7 +180,7 @@
 Network Name: OpenThread-8f28
 PAN ID: 0x8f28
 PSKc: c23a76e98f1a6483639b1ac1271e2e27
-Security Policy: 0, onrcb
+Security Policy: 0, onrc
 Done
 ```
 
@@ -359,7 +359,7 @@
 Send MGMT_ACTIVE_SET or MGMT_PENDING_SET.
 
 ```bash
-> dataset mgmtsetcommand active activetimestamp 123 securitypolicy 1 onrcb
+> dataset mgmtsetcommand active activetimestamp 123 securitypolicy 1 onrc
 Done
 ```
 
@@ -441,7 +441,7 @@
 Network Name: OpenThread-8f28
 PAN ID: 0x8f28
 PSKc: c23a76e98f1a6483639b1ac1271e2e27
-Security Policy: 0, onrcb
+Security Policy: 0, onrc
 Done
 ```
 
@@ -497,13 +497,13 @@
 
 ### securitypolicy
 
-Usage: `dataset securitypolicy [<rotationtime> [onrcbCepR]]`
+Usage: `dataset securitypolicy [<rotationtime> [onrcCepR]]`
 
 Get security policy.
 
 ```bash
 > dataset securitypolicy
-672 onrcb
+672 onrc
 Done
 ```
 
@@ -513,14 +513,13 @@
 - n: Native Commissioning using PSKc is allowed.
 - r: Thread 1.x Routers are enabled.
 - c: External Commissioner authentication is allowed using PSKc.
-- b: Thread 1.x Beacons are enabled.
 - C: Thread 1.2 Commercial Commissioning is enabled.
 - e: Thread 1.2 Autonomous Enrollment is enabled.
 - p: Thread 1.2 Network Key Provisioning is enabled.
 - R: Non-CCM routers are allowed in Thread 1.2 CCM networks.
 
 ```bash
-> dataset securitypolicy 672 onrcb
+> dataset securitypolicy 672 onrc
 Done
 ```
 
diff --git a/src/cli/README_NETDATA.md b/src/cli/README_NETDATA.md
index b119de1..68c3b43 100644
--- a/src/cli/README_NETDATA.md
+++ b/src/cli/README_NETDATA.md
@@ -27,7 +27,7 @@
    Network Name: OpenThread-8f28
    PAN ID: 0x8f28
    PSKc: c23a76e98f1a6483639b1ac1271e2e27
-   Security Policy: 0, onrcb
+   Security Policy: 0, onrc
    Done
    ```
 
diff --git a/src/cli/README_SRP.md b/src/cli/README_SRP.md
index 645f44f..a0ab5ba 100644
--- a/src/cli/README_SRP.md
+++ b/src/cli/README_SRP.md
@@ -25,7 +25,7 @@
 Network Name: OpenThread-f7af
 PAN ID: 0xf7af
 PSKc: b658e40f174e3a11be149b302ef07a0f
-Security Policy: 672, onrcb
+Security Policy: 672, onrc
 Done
 > dataset commit active
 Done
diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp
index 74713cd..ef5376f 100644
--- a/src/cli/cli.cpp
+++ b/src/cli/cli.cpp
@@ -85,7 +85,6 @@
 #include <openthread/trel.h>
 #endif
 
-#include "common/logging.hpp"
 #include "common/new.hpp"
 #include "common/string.hpp"
 #include "mac/channel_mask.hpp"
@@ -93,8 +92,6 @@
 namespace ot {
 namespace Cli {
 
-constexpr Interpreter::Command Interpreter::sCommands[];
-
 Interpreter *Interpreter::sInterpreter = nullptr;
 static OT_DEFINE_ALIGNED_VAR(sInterpreterRaw, sizeof(Interpreter), uint64_t);
 
@@ -200,7 +197,7 @@
 }
 
 #if OPENTHREAD_CONFIG_DIAG_ENABLE
-otError Interpreter::ProcessDiag(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("diag")>(Arg aArgs[])
 {
     otError error;
     char *  args[kMaxArgs];
@@ -217,24 +214,7 @@
 }
 #endif
 
-otError Interpreter::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    for (uint8_t i = 0; i < mUserCommandsLength; i++)
-    {
-        OutputLine("%s", mUserCommands[i].mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
-otError Interpreter::ProcessVersion(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("version")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -254,7 +234,7 @@
     return error;
 }
 
-otError Interpreter::ProcessReset(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("reset")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -265,9 +245,8 @@
 
 void Interpreter::ProcessLine(char *aBuf)
 {
-    Arg            args[kMaxArgs + 1];
-    const Command *command;
-    otError        error = OT_ERROR_NONE;
+    Arg     args[kMaxArgs + 1];
+    otError error = OT_ERROR_NONE;
 
     OT_ASSERT(aBuf != nullptr);
 
@@ -283,23 +262,14 @@
     LogInput(args);
 
 #if OPENTHREAD_CONFIG_DIAG_ENABLE
-    if (otDiagIsEnabled(GetInstancePtr()) && (args[0] != "diag"))
+    if (otDiagIsEnabled(GetInstancePtr()) && (args[0] != "diag") && (args[0] != "factoryreset"))
     {
         OutputLine("under diagnostics mode, execute 'diag stop' before running any other commands.");
         ExitNow(error = OT_ERROR_INVALID_STATE);
     }
 #endif
 
-    command = BinarySearch::Find(args[0].GetCString(), sCommands);
-
-    if (command != nullptr)
-    {
-        error = (this->*command->mHandler)(args + 1);
-    }
-    else
-    {
-        error = ProcessUserCommands(args);
-    }
+    error = ProcessCommand(args);
 
 exit:
     if ((error != OT_ERROR_NONE) || !args[0].IsEmpty())
@@ -432,14 +402,14 @@
 #endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
 
 #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
-otError Interpreter::ProcessHistory(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("history")>(Arg aArgs[])
 {
     return mHistory.Process(aArgs);
 }
 #endif
 
 #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
-otError Interpreter::ProcessBorderAgent(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("ba")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -471,7 +441,7 @@
 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
-otError Interpreter::ProcessBorderRouting(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("br")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     bool    enable;
@@ -514,7 +484,7 @@
 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
 
 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-otError Interpreter::ProcessBackboneRouter(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("bbr")>(Arg aArgs[])
 {
     otError                error = OT_ERROR_INVALID_COMMAND;
     otBackboneRouterConfig config;
@@ -573,13 +543,6 @@
             }
 #endif
         }
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        else if (aArgs[0] == "skipseqnuminc")
-        {
-            otBackboneRouterConfigSkipSeqNumIncrease(GetInstancePtr(), true);
-            ExitNow(error = OT_ERROR_NONE);
-        }
-#endif
         SuccessOrExit(error = ProcessBackboneRouterLocal(aArgs));
     }
 
@@ -730,7 +693,7 @@
 }
 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
 
-otError Interpreter::ProcessDomainName(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("domainname")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -748,7 +711,7 @@
 }
 
 #if OPENTHREAD_CONFIG_DUA_ENABLE
-otError Interpreter::ProcessDua(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("dua")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -787,27 +750,25 @@
 
 #endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
 
-otError Interpreter::ProcessBufferInfo(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("bufferinfo")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
     struct BufferInfoName
     {
-        const uint16_t otBufferInfo::*mNumMessagesPtr;
-        const uint16_t otBufferInfo::*mNumBuffersPtr;
-        const char *                  mName;
+        const otMessageQueueInfo otBufferInfo::*mQueuePtr;
+        const char *                            mName;
     };
 
     static const BufferInfoName kBufferInfoNames[] = {
-        {&otBufferInfo::m6loSendMessages, &otBufferInfo::m6loSendBuffers, "6lo send"},
-        {&otBufferInfo::m6loReassemblyMessages, &otBufferInfo::m6loReassemblyBuffers, "6lo reas"},
-        {&otBufferInfo::mIp6Messages, &otBufferInfo::mIp6Buffers, "ip6"},
-        {&otBufferInfo::mMplMessages, &otBufferInfo::mMplBuffers, "mpl"},
-        {&otBufferInfo::mMleMessages, &otBufferInfo::mMleBuffers, "mle"},
-        {&otBufferInfo::mArpMessages, &otBufferInfo::mArpBuffers, "arp"},
-        {&otBufferInfo::mCoapMessages, &otBufferInfo::mCoapBuffers, "coap"},
-        {&otBufferInfo::mCoapSecureMessages, &otBufferInfo::mCoapSecureBuffers, "coap secure"},
-        {&otBufferInfo::mApplicationCoapMessages, &otBufferInfo::mApplicationCoapBuffers, "application coap"},
+        {&otBufferInfo::m6loSendQueue, "6lo send"},
+        {&otBufferInfo::m6loReassemblyQueue, "6lo reas"},
+        {&otBufferInfo::mIp6Queue, "ip6"},
+        {&otBufferInfo::mMplQueue, "mpl"},
+        {&otBufferInfo::mMleQueue, "mle"},
+        {&otBufferInfo::mCoapQueue, "coap"},
+        {&otBufferInfo::mCoapSecureQueue, "coap secure"},
+        {&otBufferInfo::mApplicationCoapQueue, "application coap"},
     };
 
     otBufferInfo bufferInfo;
@@ -819,13 +780,14 @@
 
     for (const BufferInfoName &info : kBufferInfoNames)
     {
-        OutputLine("%s: %d %d", info.mName, bufferInfo.*info.mNumMessagesPtr, bufferInfo.*info.mNumBuffersPtr);
+        OutputLine("%s: %u %u %u", info.mName, (bufferInfo.*info.mQueuePtr).mNumMessages,
+                   (bufferInfo.*info.mQueuePtr).mNumBuffers, (bufferInfo.*info.mQueuePtr).mTotalBytes);
     }
 
     return OT_ERROR_NONE;
 }
 
-otError Interpreter::ProcessCcaThreshold(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("ccathreshold")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     int8_t  cca;
@@ -846,7 +808,7 @@
 }
 
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-otError Interpreter::ProcessCcm(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("ccm")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     bool    enable;
@@ -860,7 +822,7 @@
     return error;
 }
 
-otError Interpreter::ProcessThreadVersionCheck(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("tvcheck")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     bool    enable;
@@ -876,7 +838,7 @@
 
 #endif
 
-otError Interpreter::ProcessChannel(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("channel")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1013,7 +975,7 @@
 }
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessChild(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("child")>(Arg aArgs[])
 {
     otError          error = OT_ERROR_NONE;
     otChildInfo      childInfo;
@@ -1101,7 +1063,7 @@
     return error;
 }
 
-otError Interpreter::ProcessChildIp(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("childip")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1147,14 +1109,14 @@
     return error;
 }
 
-otError Interpreter::ProcessChildMax(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("childmax")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetMaxAllowedChildren, otThreadSetMaxAllowedChildren);
 }
 #endif // OPENTHREAD_FTD
 
 #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
-otError Interpreter::ProcessChildSupervision(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("childsupervision")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_ARGS;
 
@@ -1173,27 +1135,27 @@
 }
 #endif // OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
 
-otError Interpreter::ProcessChildTimeout(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("childtimeout")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetChildTimeout, otThreadSetChildTimeout);
 }
 
 #if OPENTHREAD_CONFIG_COAP_API_ENABLE
-otError Interpreter::ProcessCoap(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("coap")>(Arg aArgs[])
 {
     return mCoap.Process(aArgs);
 }
 #endif
 
 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-otError Interpreter::ProcessCoapSecure(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("coaps")>(Arg aArgs[])
 {
     return mCoapSecure.Process(aArgs);
 }
 #endif
 
 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
-otError Interpreter::ProcessCoexMetrics(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("coex")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     bool    enable;
@@ -1268,13 +1230,13 @@
 #endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessContextIdReuseDelay(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("contextreusedelay")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetContextIdReuseDelay, otThreadSetContextIdReuseDelay);
 }
 #endif
 
-otError Interpreter::ProcessCounters(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("counters")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1434,7 +1396,7 @@
 }
 
 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
-otError Interpreter::ProcessCsl(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("csl")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1467,7 +1429,7 @@
 #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessDelayTimerMin(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("delaytimermin")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1491,7 +1453,7 @@
 }
 #endif
 
-otError Interpreter::ProcessDiscover(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("discover")>(Arg aArgs[])
 {
     otError  error        = OT_ERROR_NONE;
     uint32_t scanChannels = 0;
@@ -1508,7 +1470,15 @@
     SuccessOrExit(error = otThreadDiscover(GetInstancePtr(), scanChannels, OT_PANID_BROADCAST, false, false,
                                            &Interpreter::HandleActiveScanResult, this));
 
-    OutputScanTableHeader();
+    static const char *const kScanTableTitles[] = {
+        "Network Name", "Extended PAN", "PAN", "MAC Address", "Ch", "dBm", "LQI",
+    };
+
+    static const uint8_t kScanTableColumnWidths[] = {
+        18, 18, 6, 18, 4, 5, 5,
+    };
+
+    OutputTableHeader(kScanTableTitles, kScanTableColumnWidths);
 
     error = OT_ERROR_PENDING;
 
@@ -1516,7 +1486,7 @@
     return error;
 }
 
-otError Interpreter::ProcessDns(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("dns")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -1574,6 +1544,16 @@
                                                         &Interpreter::HandleDnsAddressResponse, this, config));
         error = OT_ERROR_PENDING;
     }
+#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
+    else if (aArgs[0] == "resolve4")
+    {
+        VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+        SuccessOrExit(error = GetDnsConfig(aArgs + 2, config));
+        SuccessOrExit(error = otDnsClientResolveIp4Address(GetInstancePtr(), aArgs[1].GetCString(),
+                                                           &Interpreter::HandleDnsAddressResponse, this, config));
+        error = OT_ERROR_PENDING;
+    }
+#endif
 #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE
     else if (aArgs[0] == "browse")
     {
@@ -1801,7 +1781,7 @@
     OutputLine("");
 }
 
-otError Interpreter::ProcessEidCache(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("eidcache")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -1821,7 +1801,7 @@
 }
 #endif
 
-otError Interpreter::ProcessEui64(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("eui64")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -1837,7 +1817,7 @@
     return error;
 }
 
-otError Interpreter::ProcessExtAddress(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("extaddr")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1857,7 +1837,7 @@
     return error;
 }
 
-otError Interpreter::ProcessLog(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("log")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1896,7 +1876,7 @@
     return error;
 }
 
-otError Interpreter::ProcessExtPanId(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("extpanid")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1916,7 +1896,7 @@
     return error;
 }
 
-otError Interpreter::ProcessFactoryReset(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("factoryreset")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -1926,7 +1906,7 @@
 }
 
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-otError Interpreter::ProcessFake(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("fake")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_COMMAND;
 
@@ -1960,7 +1940,7 @@
 }
 #endif
 
-otError Interpreter::ProcessFem(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("fem")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -1997,7 +1977,7 @@
     return error;
 }
 
-otError Interpreter::ProcessIfconfig(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("ifconfig")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -2046,7 +2026,7 @@
     return Stringify(aOrigin, kOriginStrings);
 }
 
-otError Interpreter::ProcessIpAddr(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("ipaddr")>(Arg aArgs[])
 {
     otError error   = OT_ERROR_NONE;
     bool    verbose = false;
@@ -2113,7 +2093,7 @@
     return error;
 }
 
-otError Interpreter::ProcessIpMulticastAddr(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("ipmaddr")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -2127,12 +2107,12 @@
     }
     else if (aArgs[0] == "add")
     {
+        otIp6Address address;
+
         aArgs++;
 
         do
         {
-            otIp6Address address;
-
             SuccessOrExit(error = aArgs->ParseAsIp6Address(address));
             SuccessOrExit(error = otIp6SubscribeMulticastAddress(GetInstancePtr(), &address));
         }
@@ -2180,7 +2160,7 @@
     return error;
 }
 
-otError Interpreter::ProcessKeySequence(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("keysequence")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_ARGS;
 
@@ -2196,7 +2176,7 @@
     return error;
 }
 
-otError Interpreter::ProcessLeaderData(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("leaderdata")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -2216,7 +2196,7 @@
 }
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessPartitionId(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("partitionid")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_COMMAND;
 
@@ -2235,7 +2215,7 @@
     return error;
 }
 
-otError Interpreter::ProcessLeaderWeight(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("leaderweight")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetLocalLeaderWeight, otThreadSetLocalLeaderWeight);
 }
@@ -2358,13 +2338,36 @@
     return str;
 }
 
-otError Interpreter::ProcessLinkMetrics(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("linkmetrics")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_COMMAND;
 
     if (aArgs[0] == "query")
     {
-        error = ProcessLinkMetricsQuery(aArgs + 1);
+        otIp6Address  address;
+        otLinkMetrics linkMetrics;
+
+        SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
+
+        if (aArgs[2] == "single")
+        {
+            VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
+            SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3]));
+            error = otLinkMetricsQuery(GetInstancePtr(), &address, /* aSeriesId */ 0, &linkMetrics,
+                                       &Interpreter::HandleLinkMetricsReport, this);
+        }
+        else if (aArgs[2] == "forward")
+        {
+            uint8_t seriesId;
+
+            SuccessOrExit(error = aArgs[3].ParseAsUint8(seriesId));
+            error = otLinkMetricsQuery(GetInstancePtr(), &address, seriesId, nullptr,
+                                       &Interpreter::HandleLinkMetricsReport, this);
+        }
+        else
+        {
+            error = OT_ERROR_INVALID_ARGS;
+        }
     }
     else if (aArgs[0] == "mgmt")
     {
@@ -2372,38 +2375,15 @@
     }
     else if (aArgs[0] == "probe")
     {
-        error = ProcessLinkMetricsProbe(aArgs + 1);
-    }
+        otIp6Address address;
+        uint8_t      seriesId;
+        uint8_t      length;
 
-    return error;
-}
-
-otError Interpreter::ProcessLinkMetricsQuery(Arg aArgs[])
-{
-    otError       error;
-    otIp6Address  address;
-    otLinkMetrics linkMetrics;
-
-    SuccessOrExit(error = aArgs[0].ParseAsIp6Address(address));
-
-    if (aArgs[1] == "single")
-    {
-        VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
-        SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[2]));
-        error = otLinkMetricsQuery(GetInstancePtr(), &address, /* aSeriesId */ 0, &linkMetrics,
-                                   &Interpreter::HandleLinkMetricsReport, this);
-    }
-    else if (aArgs[1] == "forward")
-    {
-        uint8_t seriesId;
-
+        SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address));
         SuccessOrExit(error = aArgs[2].ParseAsUint8(seriesId));
-        error = otLinkMetricsQuery(GetInstancePtr(), &address, seriesId, nullptr, &Interpreter::HandleLinkMetricsReport,
-                                   this);
-    }
-    else
-    {
-        error = OT_ERROR_INVALID_ARGS;
+        SuccessOrExit(error = aArgs[3].ParseAsUint8(length));
+
+        error = otLinkMetricsSendLinkProbe(GetInstancePtr(), &address, seriesId, length);
     }
 
 exit:
@@ -2547,27 +2527,11 @@
     return error;
 }
 
-otError Interpreter::ProcessLinkMetricsProbe(Arg aArgs[])
-{
-    otError      error = OT_ERROR_INVALID_ARGS;
-    otIp6Address address;
-    uint8_t      seriesId = 0;
-    uint8_t      length   = 0;
-
-    SuccessOrExit(error = aArgs[0].ParseAsIp6Address(address));
-    SuccessOrExit(error = aArgs[1].ParseAsUint8(seriesId));
-    SuccessOrExit(error = aArgs[2].ParseAsUint8(length));
-
-    error = otLinkMetricsSendLinkProbe(GetInstancePtr(), &address, seriesId, length);
-
-exit:
-    return error;
-}
 #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
 
 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
 
-otError Interpreter::ProcessLocate(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("locate")>(Arg aArgs[])
 {
     otError      error = OT_ERROR_INVALID_ARGS;
     otIp6Address anycastAddress;
@@ -2618,7 +2582,7 @@
 #endif //  OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessPskc(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("pskc")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     otPskc  pskc;
@@ -2654,7 +2618,7 @@
 }
 
 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
-otError Interpreter::ProcessPskcRef(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("pskcref")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -2685,7 +2649,7 @@
 #endif // OPENTHREAD_FTD
 
 #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-otError Interpreter::ProcessMlIid(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("mliid")>(Arg aArgs[])
 {
     otError                  error = OT_ERROR_NONE;
     otIp6InterfaceIdentifier iid;
@@ -2702,7 +2666,7 @@
 
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
 
-otError Interpreter::ProcessMlr(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("mlr")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_COMMAND;
 
@@ -2774,7 +2738,7 @@
 
 #endif // (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
 
-otError Interpreter::ProcessMode(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("mode")>(Arg aArgs[])
 {
     otError          error = OT_ERROR_NONE;
     otLinkModeConfig linkMode;
@@ -2819,7 +2783,7 @@
     return error;
 }
 
-otError Interpreter::ProcessMultiRadio(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("multiradio")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -2907,7 +2871,7 @@
 #endif // OPENTHREAD_CONFIG_MULTI_RADIO
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessNeighbor(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("neighbor")>(Arg aArgs[])
 {
     otError                error = OT_ERROR_NONE;
     otNeighborInfo         neighborInfo;
@@ -2964,7 +2928,7 @@
 }
 #endif // OPENTHREAD_FTD
 
-otError Interpreter::ProcessNetstat(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("netstat")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -2987,27 +2951,22 @@
 }
 
 #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
-otError Interpreter::ProcessServiceList(void)
-{
-    otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
-    otServiceConfig       config;
-
-    while (otServerGetNextService(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
-    {
-        mNetworkData.OutputService(config);
-    }
-
-    return OT_ERROR_NONE;
-}
-
-otError Interpreter::ProcessService(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("service")>(Arg aArgs[])
 {
     otError         error = OT_ERROR_INVALID_COMMAND;
     otServiceConfig cfg;
 
     if (aArgs[0].IsEmpty())
     {
-        error = ProcessServiceList();
+        otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT;
+        otServiceConfig       config;
+
+        while (otServerGetNextService(GetInstancePtr(), &iterator, &config) == OT_ERROR_NONE)
+        {
+            mNetworkData.OutputService(config);
+        }
+
+        error = OT_ERROR_NONE;
     }
     else
     {
@@ -3043,19 +3002,19 @@
 }
 #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
 
-otError Interpreter::ProcessNetworkData(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("netdata")>(Arg aArgs[])
 {
     return mNetworkData.Process(aArgs);
 }
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessNetworkIdTimeout(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("networkidtimeout")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetNetworkIdTimeout, otThreadSetNetworkIdTimeout);
 }
 #endif
 
-otError Interpreter::ProcessNetworkKey(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("networkkey")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3079,7 +3038,7 @@
 }
 
 #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
-otError Interpreter::ProcessNetworkKeyRef(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("networkkeyref")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3100,7 +3059,7 @@
 }
 #endif
 
-otError Interpreter::ProcessNetworkName(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("networkname")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3118,7 +3077,7 @@
 }
 
 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-otError Interpreter::ProcessNetworkTime(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("networktime")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3168,7 +3127,7 @@
 }
 #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
 
-otError Interpreter::ProcessPanId(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("panid")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3184,7 +3143,7 @@
     return error;
 }
 
-otError Interpreter::ProcessParent(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("parent")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -3204,14 +3163,14 @@
 }
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessParentPriority(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("parentpriority")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetParentPriority, otThreadSetParentPriority);
 }
 #endif
 
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-otError Interpreter::ProcessRouterIdRange(Arg *aArgs)
+template <> otError Interpreter::Process<Cmd("routeridrange")>(Arg *aArgs)
 {
     uint8_t minRouterId;
     uint8_t maxRouterId;
@@ -3281,7 +3240,7 @@
     }
 }
 
-otError Interpreter::ProcessPing(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("ping")>(Arg aArgs[])
 {
     otError            error = OT_ERROR_NONE;
     otPingSenderConfig config;
@@ -3378,12 +3337,12 @@
 
 #endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE
 
-otError Interpreter::ProcessPollPeriod(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("pollperiod")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otLinkGetPollPeriod, otLinkSetPollPeriod);
 }
 
-otError Interpreter::ProcessPromiscuous(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("promiscuous")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3566,7 +3525,7 @@
     return error;
 }
 
-otError Interpreter::ProcessPrefix(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("prefix")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3605,7 +3564,19 @@
     }
     else if (aArgs[0] == "meshlocal")
     {
-        OutputIp6PrefixLine(*otThreadGetMeshLocalPrefix(GetInstancePtr()));
+        if (aArgs[1].IsEmpty())
+        {
+            OutputIp6PrefixLine(*otThreadGetMeshLocalPrefix(GetInstancePtr()));
+        }
+        else
+        {
+            otIp6Prefix prefix;
+
+            SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix));
+            VerifyOrExit(prefix.mLength == OT_IP6_PREFIX_BITSIZE, error = OT_ERROR_INVALID_ARGS);
+            error =
+                otThreadSetMeshLocalPrefix(GetInstancePtr(), reinterpret_cast<otMeshLocalPrefix *>(&prefix.mPrefix));
+        }
     }
     else
     {
@@ -3618,14 +3589,14 @@
 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessPreferRouterId(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("preferrouterid")>(Arg aArgs[])
 {
     return ProcessSet(aArgs, otThreadSetPreferredRouterId);
 }
 #endif
 
 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
-otError Interpreter::ProcessRadioFilter(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("radiofilter")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3646,7 +3617,7 @@
 }
 #endif
 
-otError Interpreter::ProcessRcp(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("rcp")>(Arg aArgs[])
 {
     otError     error   = OT_ERROR_NONE;
     const char *version = otPlatRadioGetVersionString(GetInstancePtr());
@@ -3666,7 +3637,7 @@
     return error;
 }
 
-otError Interpreter::ProcessRegion(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("region")>(Arg aArgs[])
 {
     otError  error = OT_ERROR_NONE;
     uint16_t regionCode;
@@ -3690,13 +3661,13 @@
 }
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessReleaseRouterId(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("releaserouterid")>(Arg aArgs[])
 {
     return ProcessSet(aArgs, otThreadReleaseRouterId);
 }
 #endif
 
-otError Interpreter::ProcessRloc16(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("rloc16")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -3747,7 +3718,7 @@
     return error;
 }
 
-otError Interpreter::ProcessRoute(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("route")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3786,7 +3757,7 @@
 #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessRouter(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("router")>(Arg aArgs[])
 {
     otError      error = OT_ERROR_NONE;
     otRouterInfo routerInfo;
@@ -3871,12 +3842,12 @@
     return error;
 }
 
-otError Interpreter::ProcessRouterDowngradeThreshold(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("routerdowngradethreshold")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetRouterDowngradeThreshold, otThreadSetRouterDowngradeThreshold);
 }
 
-otError Interpreter::ProcessRouterEligible(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("routereligible")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -3896,18 +3867,18 @@
     return error;
 }
 
-otError Interpreter::ProcessRouterSelectionJitter(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("routerselectionjitter")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetRouterSelectionJitter, otThreadSetRouterSelectionJitter);
 }
 
-otError Interpreter::ProcessRouterUpgradeThreshold(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("routerupgradethreshold")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetRouterUpgradeThreshold, otThreadSetRouterUpgradeThreshold);
 }
 #endif // OPENTHREAD_FTD
 
-otError Interpreter::ProcessScan(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("scan")>(Arg aArgs[])
 {
     otError  error        = OT_ERROR_NONE;
     uint32_t scanChannels = 0;
@@ -3946,7 +3917,11 @@
     }
     else
     {
-        OutputScanTableHeader();
+        static const char *const kScanTableTitles[]       = {"PAN", "MAC Address", "Ch", "dBm", "LQI"};
+        static const uint8_t     kScanTableColumnWidths[] = {6, 18, 4, 5, 5};
+
+        OutputTableHeader(kScanTableTitles, kScanTableColumnWidths);
+
         SuccessOrExit(error = otLinkActiveScan(GetInstancePtr(), scanChannels, scanDuration,
                                                &Interpreter::HandleActiveScanResult, this));
     }
@@ -3957,19 +3932,6 @@
     return error;
 }
 
-void Interpreter::OutputScanTableHeader(void)
-{
-    static const char *const kScanTableTitles[] = {
-        "J", "Network Name", "Extended PAN", "PAN", "MAC Address", "Ch", "dBm", "LQI",
-    };
-
-    static const uint8_t kScanTableColumnWidths[] = {
-        3, 18, 18, 6, 18, 4, 5, 5,
-    };
-
-    OutputTableHeader(kScanTableTitles, kScanTableColumnWidths);
-}
-
 void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult, void *aContext)
 {
     static_cast<Interpreter *>(aContext)->HandleActiveScanResult(aResult);
@@ -3983,13 +3945,14 @@
         ExitNow();
     }
 
-    OutputFormat("| %d ", aResult->mIsJoinable);
+    if (aResult->mDiscover)
+    {
+        OutputFormat("| %-16s ", aResult->mNetworkName.m8);
 
-    OutputFormat("| %-16s ", aResult->mNetworkName.m8);
-
-    OutputFormat("| ");
-    OutputBytes(aResult->mExtendedPanId.m8);
-    OutputFormat(" ");
+        OutputFormat("| ");
+        OutputBytes(aResult->mExtendedPanId.m8);
+        OutputFormat(" ");
+    }
 
     OutputFormat("| %04x | ", aResult->mPanId);
     OutputExtAddress(aResult->mExtAddress);
@@ -4020,7 +3983,7 @@
     return;
 }
 
-otError Interpreter::ProcessSingleton(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("singleton")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -4030,7 +3993,7 @@
 }
 
 #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
-otError Interpreter::ProcessSntp(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("sntp")>(Arg aArgs[])
 {
     otError          error = OT_ERROR_NONE;
     uint16_t         port  = OT_SNTP_DEFAULT_SERVER_PORT;
@@ -4100,7 +4063,7 @@
 #endif // OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
 
 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
-otError Interpreter::ProcessSrp(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("srp")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -4135,7 +4098,7 @@
 }
 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
 
-otError Interpreter::ProcessState(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("state")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -4169,7 +4132,7 @@
     return error;
 }
 
-otError Interpreter::ProcessThread(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("thread")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -4193,12 +4156,12 @@
     return error;
 }
 
-otError Interpreter::ProcessDataset(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("dataset")>(Arg aArgs[])
 {
     return mDataset.Process(aArgs);
 }
 
-otError Interpreter::ProcessTxPower(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("txpower")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     int8_t  power;
@@ -4219,18 +4182,18 @@
 }
 
 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
-otError Interpreter::ProcessTcp(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("tcp")>(Arg aArgs[])
 {
     return mTcp.Process(aArgs);
 }
 #endif
 
-otError Interpreter::ProcessUdp(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("udp")>(Arg aArgs[])
 {
     return mUdp.Process(aArgs);
 }
 
-otError Interpreter::ProcessUnsecurePort(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("unsecureport")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -4275,7 +4238,7 @@
 }
 
 #if OPENTHREAD_CONFIG_UPTIME_ENABLE
-otError Interpreter::ProcessUptime(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("uptime")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -4300,28 +4263,28 @@
 #endif
 
 #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
-otError Interpreter::ProcessCommissioner(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("commissioner")>(Arg aArgs[])
 {
     return mCommissioner.Process(aArgs);
 }
 #endif
 
 #if OPENTHREAD_CONFIG_JOINER_ENABLE
-otError Interpreter::ProcessJoiner(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("joiner")>(Arg aArgs[])
 {
     return mJoiner.Process(aArgs);
 }
 #endif
 
 #if OPENTHREAD_FTD
-otError Interpreter::ProcessJoinerPort(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("joinerport")>(Arg aArgs[])
 {
     return ProcessGetSet(aArgs, otThreadGetJoinerUdpPort, otThreadSetJoinerUdpPort);
 }
 #endif
 
 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
-otError Interpreter::ProcessMacFilter(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("macfilter")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -4569,7 +4532,7 @@
 
 #endif // OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
 
-otError Interpreter::ProcessMac(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("mac")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -4620,7 +4583,7 @@
 }
 
 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
-otError Interpreter::ProcessTrel(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("trel")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     bool    enable;
@@ -4715,7 +4678,7 @@
 #endif
 
 #if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
-otError Interpreter::ProcessNetworkDiagnostic(Arg aArgs[])
+template <> otError Interpreter::Process<Cmd("networkdiagnostic")>(Arg aArgs[])
 {
     otError      error = OT_ERROR_NONE;
     otIp6Address address;
@@ -4998,6 +4961,251 @@
     mTimer.Start(aTimeoutMilli);
 }
 
+otError Interpreter::ProcessCommand(Arg aArgs[])
+{
+#define CmdEntry(aCommandString)                                   \
+    {                                                              \
+        aCommandString, &Interpreter::Process<Cmd(aCommandString)> \
+    }
+
+    static constexpr Command kCommands[] = {
+#if OPENTHREAD_FTD || OPENTHREAD_MTD
+#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
+        CmdEntry("ba"),
+#endif
+#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
+        CmdEntry("bbr"),
+#endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+        CmdEntry("br"),
+#endif
+        CmdEntry("bufferinfo"),
+        CmdEntry("ccathreshold"),
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
+        CmdEntry("ccm"),
+#endif
+        CmdEntry("channel"),
+#if OPENTHREAD_FTD
+        CmdEntry("child"),
+        CmdEntry("childip"),
+        CmdEntry("childmax"),
+#endif
+#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
+        CmdEntry("childsupervision"),
+#endif
+        CmdEntry("childtimeout"),
+#if OPENTHREAD_CONFIG_COAP_API_ENABLE
+        CmdEntry("coap"),
+#endif
+#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
+        CmdEntry("coaps"),
+#endif
+#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
+        CmdEntry("coex"),
+#endif
+#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
+        CmdEntry("commissioner"),
+#endif
+#if OPENTHREAD_FTD
+        CmdEntry("contextreusedelay"),
+#endif
+        CmdEntry("counters"),
+#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
+        CmdEntry("csl"),
+#endif
+        CmdEntry("dataset"),
+#if OPENTHREAD_FTD
+        CmdEntry("delaytimermin"),
+#endif
+#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
+#if OPENTHREAD_CONFIG_DIAG_ENABLE
+        CmdEntry("diag"),
+#endif
+#if OPENTHREAD_FTD || OPENTHREAD_MTD
+        CmdEntry("discover"),
+        CmdEntry("dns"),
+#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
+        CmdEntry("domainname"),
+#endif
+#if OPENTHREAD_CONFIG_DUA_ENABLE
+        CmdEntry("dua"),
+#endif
+#if OPENTHREAD_FTD
+        CmdEntry("eidcache"),
+#endif
+        CmdEntry("eui64"),
+        CmdEntry("extaddr"),
+        CmdEntry("extpanid"),
+        CmdEntry("factoryreset"),
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
+        CmdEntry("fake"),
+#endif
+        CmdEntry("fem"),
+#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
+#if OPENTHREAD_FTD || OPENTHREAD_MTD
+#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
+        CmdEntry("history"),
+#endif
+        CmdEntry("ifconfig"),
+        CmdEntry("ipaddr"),
+        CmdEntry("ipmaddr"),
+#if OPENTHREAD_CONFIG_JOINER_ENABLE
+        CmdEntry("joiner"),
+#endif
+#if OPENTHREAD_FTD
+        CmdEntry("joinerport"),
+#endif
+        CmdEntry("keysequence"),
+        CmdEntry("leaderdata"),
+#if OPENTHREAD_FTD
+        CmdEntry("leaderweight"),
+#endif
+#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
+        CmdEntry("linkmetrics"),
+#endif
+#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
+        CmdEntry("locate"),
+#endif
+        CmdEntry("log"),
+        CmdEntry("mac"),
+#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
+        CmdEntry("macfilter"),
+#endif
+#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
+        CmdEntry("mliid"),
+#endif
+#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
+        CmdEntry("mlr"),
+#endif
+        CmdEntry("mode"),
+        CmdEntry("multiradio"),
+#if OPENTHREAD_FTD
+        CmdEntry("neighbor"),
+#endif
+        CmdEntry("netdata"),
+        CmdEntry("netstat"),
+#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
+        CmdEntry("networkdiagnostic"),
+#endif
+#if OPENTHREAD_FTD
+        CmdEntry("networkidtimeout"),
+#endif
+        CmdEntry("networkkey"),
+#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
+        CmdEntry("networkkeyref"),
+#endif
+        CmdEntry("networkname"),
+#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
+        CmdEntry("networktime"),
+#endif
+        CmdEntry("panid"),
+        CmdEntry("parent"),
+#if OPENTHREAD_FTD
+        CmdEntry("parentpriority"),
+        CmdEntry("partitionid"),
+#endif
+#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
+        CmdEntry("ping"),
+#endif
+        CmdEntry("pollperiod"),
+#if OPENTHREAD_FTD
+        CmdEntry("preferrouterid"),
+#endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
+        CmdEntry("prefix"),
+#endif
+        CmdEntry("promiscuous"),
+#if OPENTHREAD_FTD
+        CmdEntry("pskc"),
+#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
+        CmdEntry("pskcref"),
+#endif
+#endif
+#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
+        CmdEntry("radiofilter"),
+#endif
+        CmdEntry("rcp"),
+        CmdEntry("region"),
+#if OPENTHREAD_FTD
+        CmdEntry("releaserouterid"),
+#endif
+#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
+        CmdEntry("reset"),
+#if OPENTHREAD_FTD || OPENTHREAD_MTD
+        CmdEntry("rloc16"),
+#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
+        CmdEntry("route"),
+#endif
+#if OPENTHREAD_FTD
+        CmdEntry("router"),
+        CmdEntry("routerdowngradethreshold"),
+        CmdEntry("routereligible"),
+#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
+        CmdEntry("routeridrange"),
+#endif
+        CmdEntry("routerselectionjitter"),
+        CmdEntry("routerupgradethreshold"),
+#endif
+        CmdEntry("scan"),
+#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
+        CmdEntry("service"),
+#endif
+        CmdEntry("singleton"),
+#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
+        CmdEntry("sntp"),
+#endif
+#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
+        CmdEntry("srp"),
+#endif
+        CmdEntry("state"),
+#if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
+        CmdEntry("tcp"),
+#endif
+        CmdEntry("thread"),
+#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
+        CmdEntry("trel"),
+#endif
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
+        CmdEntry("tvcheck"),
+#endif
+        CmdEntry("txpower"),
+        CmdEntry("udp"),
+        CmdEntry("unsecureport"),
+#if OPENTHREAD_CONFIG_UPTIME_ENABLE
+        CmdEntry("uptime"),
+#endif
+#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
+        CmdEntry("version"),
+    };
+
+#undef CmdEntry
+
+    static_assert(BinarySearch::IsSorted(kCommands), "Command Table is not sorted");
+
+    otError        error   = OT_ERROR_NONE;
+    const Command *command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
+
+    if (command != nullptr)
+    {
+        error = (this->*command->mHandler)(aArgs + 1);
+    }
+    else if (aArgs[0] == "help")
+    {
+        OutputCommandTable(kCommands);
+
+        for (uint8_t i = 0; i < mUserCommandsLength; i++)
+        {
+            OutputLine("%s", mUserCommands[i].mName);
+        }
+    }
+    else
+    {
+        error = ProcessUserCommands(aArgs);
+    }
+
+    return error;
+}
+
 extern "C" void otCliInit(otInstance *aInstance, otCliOutputCallback aCallback, void *aContext)
 {
     Interpreter::Initialize(aInstance, aCallback, aContext);
@@ -5049,21 +5257,6 @@
     return;
 }
 
-extern "C" void otCliPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine)
-{
-    OT_UNUSED_VARIABLE(aLogLevel);
-    OT_UNUSED_VARIABLE(aLogRegion);
-
-    VerifyOrExit(Interpreter::IsInitialized());
-
-    Interpreter::GetInterpreter().SetEmittingCommandOutput(false);
-    Interpreter::GetInterpreter().OutputLine(aLogLine);
-    Interpreter::GetInterpreter().SetEmittingCommandOutput(true);
-
-exit:
-    return;
-}
-
 } // namespace Cli
 } // namespace ot
 
diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp
index 0f57847..90144d1 100644
--- a/src/cli/cli.hpp
+++ b/src/cli/cli.hpp
@@ -46,6 +46,7 @@
 #include <openthread/instance.h>
 #include <openthread/ip6.h>
 #include <openthread/link.h>
+#include <openthread/logging.h>
 #include <openthread/ping_sender.h>
 #include <openthread/sntp.h>
 #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
@@ -89,7 +90,6 @@
 namespace Cli {
 
 extern "C" void otCliPlatLogv(otLogLevel, otLogRegion, const char *, va_list);
-extern "C" void otCliPlatLogLine(otLogLevel, otLogRegion, const char *);
 extern "C" void otCliAppendResult(otError aError);
 extern "C" void otCliOutputBytes(const uint8_t *aBytes, uint8_t aLength);
 extern "C" void otCliOutputFormat(const char *aFmt, ...);
@@ -107,7 +107,6 @@
     friend class SrpClient;
 #endif
     friend void otCliPlatLogv(otLogLevel, otLogRegion, const char *, va_list);
-    friend void otCliPlatLogLine(otLogLevel, otLogRegion, const char *);
     friend void otCliAppendResult(otError aError);
     friend void otCliOutputBytes(const uint8_t *aBytes, uint8_t aLength);
     friend void otCliOutputFormat(const char *aFmt, ...);
@@ -166,7 +165,7 @@
     /**
      * This static method checks a given argument string against "enable" or "disable" commands.
      *
-     * @param[in]  aArgs    The argument string to parse.
+     * @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.
      *
@@ -179,9 +178,9 @@
     /**
      * This method sets the user command table.
      *
-     * @param[in]  aUserCommands  A pointer to an array with user commands.
-     * @param[in]  aLength        @p aUserCommands length.
-     * @param[in]  aContext       @p aUserCommands length.
+     * @param[in]  aCommands  A pointer to an array with user commands.
+     * @param[in]  aLength    @p aUserCommands length.
+     * @param[in]  aContext   @p aUserCommands length.
      *
      */
     void SetUserCommands(const otCliCommand *aCommands, uint8_t aLength, void *aContext);
@@ -317,34 +316,15 @@
     static otError ParseRoute(Arg aArgs[], otExternalRouteConfig &aConfig);
 #endif
 
-    // Process methods on FTD/MTD/RCP
-#if OPENTHREAD_CONFIG_DIAG_ENABLE
-    otError ProcessDiag(Arg aArgs[]);
-#endif
-    otError ProcessHelp(Arg aArgs[]);
-    otError ProcessHistory(Arg aArgs[]);
-    otError ProcessReset(Arg aArgs[]);
+    otError ProcessCommand(Arg aArgs[]);
+
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
+
     otError ProcessUserCommands(Arg aArgs[]);
-    otError ProcessVersion(Arg aArgs[]);
 
-    // Process methods only on FTD/MTD
 #if OPENTHREAD_FTD || OPENTHREAD_MTD
-    otError ProcessCcaThreshold(Arg aArgs[]);
-#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-    otError ProcessCcm(Arg aArgs[]);
-    otError ProcessThreadVersionCheck(Arg aArgs[]);
-#endif
-    otError ProcessBufferInfo(Arg aArgs[]);
-    otError ProcessChannel(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
-    otError ProcessBorderAgent(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
-    otError ProcessBorderRouting(Arg aArgs[]);
-#endif
-#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-    otError ProcessBackboneRouter(Arg aArgs[]);
 
+#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
     otError ProcessBackboneRouterLocal(Arg aArgs[]);
 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
@@ -352,86 +332,18 @@
     void    PrintMulticastListenersTable(void);
 #endif
 #endif
-
-    otError ProcessDomainName(Arg aArgs[]);
-
-#if OPENTHREAD_CONFIG_DUA_ENABLE
-    otError ProcessDua(Arg aArgs[]);
 #endif
 
-#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-
 #if OPENTHREAD_FTD
-    otError ProcessChild(Arg aArgs[]);
-    otError ProcessChildIp(Arg aArgs[]);
-    otError ProcessChildMax(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
-    otError ProcessChildSupervision(Arg aArgs[]);
-#endif
-    otError ProcessChildTimeout(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_COAP_API_ENABLE
-    otError ProcessCoap(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-    otError ProcessCoapSecure(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
-    otError ProcessCoexMetrics(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
-    otError ProcessCommissioner(Arg aArgs[]);
-#endif
-#if OPENTHREAD_FTD
-    otError ProcessContextIdReuseDelay(Arg aArgs[]);
-#endif
-    otError ProcessCounters(Arg aArgs[]);
-    otError ProcessCsl(Arg aArgs[]);
-#if OPENTHREAD_FTD
-    otError ProcessDelayTimerMin(Arg aArgs[]);
-#endif
-    otError ProcessDiscover(Arg aArgs[]);
-    otError ProcessDns(Arg aArgs[]);
-#if OPENTHREAD_FTD
-    void    OutputEidCacheEntry(const otCacheEntryInfo &aEntry);
-    otError ProcessEidCache(Arg aArgs[]);
-#endif
-    otError ProcessEui64(Arg aArgs[]);
-    otError ProcessLog(Arg aArgs[]);
-    otError ProcessExtAddress(Arg aArgs[]);
-    otError ProcessExtPanId(Arg aArgs[]);
-    otError ProcessFactoryReset(Arg aArgs[]);
-#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-    otError ProcessFake(Arg aArgs[]);
-#endif
-    otError ProcessFem(Arg aArgs[]);
-    otError ProcessIfconfig(Arg aArgs[]);
-    otError ProcessIpAddr(Arg aArgs[]);
-    otError ProcessIpMulticastAddr(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_JOINER_ENABLE
-    otError ProcessJoiner(Arg aArgs[]);
-#endif
-#if OPENTHREAD_FTD
-    otError ProcessJoinerPort(Arg aArgs[]);
-#endif
-    otError ProcessKeySequence(Arg aArgs[]);
-    otError ProcessLeaderData(Arg aArgs[]);
-#if OPENTHREAD_FTD
-    otError ProcessPartitionId(Arg aArgs[]);
-    otError ProcessLeaderWeight(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-    otError ProcessMlIid(Arg aArgs[]);
+    void OutputEidCacheEntry(const otCacheEntryInfo &aEntry);
 #endif
 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
-    otError ProcessLinkMetrics(Arg aArgs[]);
     otError ProcessLinkMetricsQuery(Arg aArgs[]);
     otError ProcessLinkMetricsMgmt(Arg aArgs[]);
     otError ProcessLinkMetricsProbe(Arg aArgs[]);
     otError ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, const Arg &aFlags);
 #endif
 #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
-    otError     ProcessLocate(Arg aArgs[]);
     static void HandleLocateResult(void *              aContext,
                                    otError             aError,
                                    const otIp6Address *aMeshLocalAddress,
@@ -439,8 +351,6 @@
     void        HandleLocateResult(otError aError, const otIp6Address *aMeshLocalAddress, uint16_t aRloc16);
 #endif
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
-    otError ProcessMlr(Arg aArgs[]);
-
     static void HandleMlrRegResult(void *              aContext,
                                    otError             aError,
                                    uint8_t             aMlrStatus,
@@ -451,114 +361,22 @@
                                    const otIp6Address *aFailedAddresses,
                                    uint8_t             aFailedAddressNum);
 #endif
-    otError ProcessMode(Arg aArgs[]);
-    otError ProcessMultiRadio(Arg aArgs[]);
 #if OPENTHREAD_CONFIG_MULTI_RADIO
     void OutputMultiRadioInfo(const otMultiRadioNeighborInfo &aMultiRadioInfo);
 #endif
-#if OPENTHREAD_FTD
-    otError ProcessNeighbor(Arg aArgs[]);
-#endif
-    otError ProcessNetworkData(Arg aArgs[]);
-    otError ProcessNetstat(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
-    otError ProcessService(Arg aArgs[]);
-    otError ProcessServiceList(void);
-#endif
-#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
-    otError ProcessNetworkDiagnostic(Arg aArgs[]);
-#endif
-#if OPENTHREAD_FTD
-    otError ProcessNetworkIdTimeout(Arg aArgs[]);
-#endif
-    otError ProcessNetworkKey(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
-    otError ProcessNetworkKeyRef(Arg aArgs[]);
-#endif
-    otError ProcessNetworkName(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-    otError ProcessNetworkTime(Arg aArgs[]);
-#endif
-    otError ProcessPanId(Arg aArgs[]);
-    otError ProcessParent(Arg aArgs[]);
-#if OPENTHREAD_FTD
-    otError ProcessParentPriority(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
-    otError ProcessPing(Arg aArgs[]);
-#endif
-    otError ProcessPollPeriod(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
-    otError ProcessPrefix(Arg aArgs[]);
-#endif
-    otError ProcessPromiscuous(Arg aArgs[]);
-#if OPENTHREAD_FTD
-    otError ProcessPreferRouterId(Arg aArgs[]);
-    otError ProcessPskc(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
-    otError ProcessPskcRef(Arg aArgs[]);
-#endif
-#endif
-#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
-    otError ProcessRadioFilter(Arg aArgs[]);
-#endif
-    otError ProcessRcp(Arg aArgs[]);
-    otError ProcessRegion(Arg aArgs[]);
-#if OPENTHREAD_FTD
-    otError ProcessReleaseRouterId(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
-    otError ProcessRoute(Arg aArgs[]);
-#endif
-#if OPENTHREAD_FTD
-    otError ProcessRouter(Arg aArgs[]);
-    otError ProcessRouterDowngradeThreshold(Arg aArgs[]);
-    otError ProcessRouterEligible(Arg aArgs[]);
-    otError ProcessRouterSelectionJitter(Arg aArgs[]);
-    otError ProcessRouterUpgradeThreshold(Arg aArgs[]);
-#endif
-    otError ProcessRloc16(Arg aArgs[]);
-    otError ProcessScan(Arg aArgs[]);
-    otError ProcessSingleton(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
-    otError ProcessSntp(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
-    otError ProcessSrp(Arg aArgs[]);
-#endif
-    otError ProcessState(Arg aArgs[]);
-    otError ProcessThread(Arg aArgs[]);
-    otError ProcessDataset(Arg aArgs[]);
-    otError ProcessTxPower(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
-    otError ProcessTcp(Arg aArgs[]);
-#endif
-    otError ProcessUdp(Arg aArgs[]);
-    otError ProcessUnsecurePort(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_UPTIME_ENABLE
-    otError ProcessUptime(Arg aArgs[]);
-#endif
+
 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
-    otError            ProcessMacFilter(Arg aArgs[]);
     void               PrintMacFilter(void);
     otError            ProcessMacFilterAddress(Arg aArgs[]);
     otError            ProcessMacFilterRss(Arg aArgs[]);
     void               OutputMacFilterEntry(const otMacFilterEntry &aEntry);
     static const char *MacFilterAddressModeToString(otMacFilterAddressMode aMode);
 #endif
-    otError ProcessMac(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
-    otError ProcessTrel(Arg aArgs[]);
-#endif
-#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-    otError ProcessRouterIdRange(Arg *aArgs);
-#endif
 
 #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
     static void HandlePingReply(const otPingSenderReply *aReply, void *aContext);
     static void HandlePingStatistics(const otPingSenderStatistics *aStatistics, void *aContext);
 #endif
-    void        OutputScanTableHeader(void);
     static void HandleActiveScanResult(otActiveScanResult *aResult, void *aContext);
     static void HandleEnergyScanResult(otEnergyScanResult *aResult, void *aContext);
     static void HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx, void *aContext);
@@ -647,221 +465,6 @@
     static void HandleTimer(Timer &aTimer);
     void        HandleTimer(void);
 
-    // Commands supported by radio:
-    // [diag, help, reset, version]
-    static constexpr Command sCommands[] = {
-#if OPENTHREAD_FTD || OPENTHREAD_MTD
-#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE
-        {"ba", &Interpreter::ProcessBorderAgent},
-#endif
-#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-        {"bbr", &Interpreter::ProcessBackboneRouter},
-#endif
-#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
-        {"br", &Interpreter::ProcessBorderRouting},
-#endif
-        {"bufferinfo", &Interpreter::ProcessBufferInfo},
-        {"ccathreshold", &Interpreter::ProcessCcaThreshold},
-#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        {"ccm", &Interpreter::ProcessCcm},
-#endif
-        {"channel", &Interpreter::ProcessChannel},
-#if OPENTHREAD_FTD
-        {"child", &Interpreter::ProcessChild},
-        {"childip", &Interpreter::ProcessChildIp},
-        {"childmax", &Interpreter::ProcessChildMax},
-#endif
-#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
-        {"childsupervision", &Interpreter::ProcessChildSupervision},
-#endif
-        {"childtimeout", &Interpreter::ProcessChildTimeout},
-#if OPENTHREAD_CONFIG_COAP_API_ENABLE
-        {"coap", &Interpreter::ProcessCoap},
-#endif
-#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
-        {"coaps", &Interpreter::ProcessCoapSecure},
-#endif
-#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
-        {"coex", &Interpreter::ProcessCoexMetrics},
-#endif
-#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD
-        {"commissioner", &Interpreter::ProcessCommissioner},
-#endif
-#if OPENTHREAD_FTD
-        {"contextreusedelay", &Interpreter::ProcessContextIdReuseDelay},
-#endif
-        {"counters", &Interpreter::ProcessCounters},
-#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
-        {"csl", &Interpreter::ProcessCsl},
-#endif
-        {"dataset", &Interpreter::ProcessDataset},
-#if OPENTHREAD_FTD
-        {"delaytimermin", &Interpreter::ProcessDelayTimerMin},
-#endif
-#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
-#if OPENTHREAD_CONFIG_DIAG_ENABLE
-        {"diag", &Interpreter::ProcessDiag},
-#endif
-#if OPENTHREAD_FTD || OPENTHREAD_MTD
-        {"discover", &Interpreter::ProcessDiscover},
-        {"dns", &Interpreter::ProcessDns},
-#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
-        {"domainname", &Interpreter::ProcessDomainName},
-#endif
-#if OPENTHREAD_CONFIG_DUA_ENABLE
-        {"dua", &Interpreter::ProcessDua},
-#endif
-#if OPENTHREAD_FTD
-        {"eidcache", &Interpreter::ProcessEidCache},
-#endif
-        {"eui64", &Interpreter::ProcessEui64},
-        {"extaddr", &Interpreter::ProcessExtAddress},
-        {"extpanid", &Interpreter::ProcessExtPanId},
-        {"factoryreset", &Interpreter::ProcessFactoryReset},
-#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        {"fake", &Interpreter::ProcessFake},
-#endif
-        {"fem", &Interpreter::ProcessFem},
-#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
-        {"help", &Interpreter::ProcessHelp},
-#if OPENTHREAD_FTD || OPENTHREAD_MTD
-#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
-        {"history", &Interpreter::ProcessHistory},
-#endif
-        {"ifconfig", &Interpreter::ProcessIfconfig},
-        {"ipaddr", &Interpreter::ProcessIpAddr},
-        {"ipmaddr", &Interpreter::ProcessIpMulticastAddr},
-#if OPENTHREAD_CONFIG_JOINER_ENABLE
-        {"joiner", &Interpreter::ProcessJoiner},
-#endif
-#if OPENTHREAD_FTD
-        {"joinerport", &Interpreter::ProcessJoinerPort},
-#endif
-        {"keysequence", &Interpreter::ProcessKeySequence},
-        {"leaderdata", &Interpreter::ProcessLeaderData},
-#if OPENTHREAD_FTD
-        {"leaderweight", &Interpreter::ProcessLeaderWeight},
-#endif
-#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
-        {"linkmetrics", &Interpreter::ProcessLinkMetrics},
-#endif
-#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE
-        {"locate", &Interpreter::ProcessLocate},
-#endif
-        {"log", &Interpreter::ProcessLog},
-        {"mac", &Interpreter::ProcessMac},
-#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
-        {"macfilter", &Interpreter::ProcessMacFilter},
-#endif
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        {"mliid", &Interpreter::ProcessMlIid},
-#endif
-#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
-        {"mlr", &Interpreter::ProcessMlr},
-#endif
-        {"mode", &Interpreter::ProcessMode},
-        {"multiradio", &Interpreter::ProcessMultiRadio},
-#if OPENTHREAD_FTD
-        {"neighbor", &Interpreter::ProcessNeighbor},
-#endif
-        {"netdata", &Interpreter::ProcessNetworkData},
-        {"netstat", &Interpreter::ProcessNetstat},
-#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE
-        {"networkdiagnostic", &Interpreter::ProcessNetworkDiagnostic},
-#endif
-#if OPENTHREAD_FTD
-        {"networkidtimeout", &Interpreter::ProcessNetworkIdTimeout},
-#endif
-        {"networkkey", &Interpreter::ProcessNetworkKey},
-#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
-        {"networkkeyref", &Interpreter::ProcessNetworkKeyRef},
-#endif
-        {"networkname", &Interpreter::ProcessNetworkName},
-#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
-        {"networktime", &Interpreter::ProcessNetworkTime},
-#endif
-        {"panid", &Interpreter::ProcessPanId},
-        {"parent", &Interpreter::ProcessParent},
-#if OPENTHREAD_FTD
-        {"parentpriority", &Interpreter::ProcessParentPriority},
-        {"partitionid", &Interpreter::ProcessPartitionId},
-#endif
-#if OPENTHREAD_CONFIG_PING_SENDER_ENABLE
-        {"ping", &Interpreter::ProcessPing},
-#endif
-        {"pollperiod", &Interpreter::ProcessPollPeriod},
-#if OPENTHREAD_FTD
-        {"preferrouterid", &Interpreter::ProcessPreferRouterId},
-#endif
-#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
-        {"prefix", &Interpreter::ProcessPrefix},
-#endif
-        {"promiscuous", &Interpreter::ProcessPromiscuous},
-#if OPENTHREAD_FTD
-        {"pskc", &Interpreter::ProcessPskc},
-#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE
-        {"pskcref", &Interpreter::ProcessPskcRef},
-#endif
-#endif
-#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
-        {"radiofilter", &Interpreter::ProcessRadioFilter},
-#endif
-        {"rcp", &Interpreter::ProcessRcp},
-        {"region", &Interpreter::ProcessRegion},
-#if OPENTHREAD_FTD
-        {"releaserouterid", &Interpreter::ProcessReleaseRouterId},
-#endif
-#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
-        {"reset", &Interpreter::ProcessReset},
-#if OPENTHREAD_FTD || OPENTHREAD_MTD
-        {"rloc16", &Interpreter::ProcessRloc16},
-#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
-        {"route", &Interpreter::ProcessRoute},
-#endif
-#if OPENTHREAD_FTD
-        {"router", &Interpreter::ProcessRouter},
-        {"routerdowngradethreshold", &Interpreter::ProcessRouterDowngradeThreshold},
-        {"routereligible", &Interpreter::ProcessRouterEligible},
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        {"routeridrange", &Interpreter::ProcessRouterIdRange},
-#endif
-        {"routerselectionjitter", &Interpreter::ProcessRouterSelectionJitter},
-        {"routerupgradethreshold", &Interpreter::ProcessRouterUpgradeThreshold},
-#endif
-        {"scan", &Interpreter::ProcessScan},
-#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
-        {"service", &Interpreter::ProcessService},
-#endif
-        {"singleton", &Interpreter::ProcessSingleton},
-#if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE
-        {"sntp", &Interpreter::ProcessSntp},
-#endif
-#if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE || OPENTHREAD_CONFIG_SRP_SERVER_ENABLE
-        {"srp", &Interpreter::ProcessSrp},
-#endif
-        {"state", &Interpreter::ProcessState},
-#if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE
-        {"tcp", &Interpreter::ProcessTcp},
-#endif
-        {"thread", &Interpreter::ProcessThread},
-#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
-        {"trel", &Interpreter::ProcessTrel},
-#endif
-#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        {"tvcheck", &Interpreter::ProcessThreadVersionCheck},
-#endif
-        {"txpower", &Interpreter::ProcessTxPower},
-        {"udp", &Interpreter::ProcessUdp},
-        {"unsecureport", &Interpreter::ProcessUnsecurePort},
-#if OPENTHREAD_CONFIG_UPTIME_ENABLE
-        {"uptime", &Interpreter::ProcessUptime},
-#endif
-#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
-        {"version", &Interpreter::ProcessVersion},
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
-
     const otCliCommand *mUserCommands;
     uint8_t             mUserCommandsLength;
     void *              mUserCommandsContext;
diff --git a/src/cli/cli_coap.cpp b/src/cli/cli_coap.cpp
index 20e88cf..5d7c260 100644
--- a/src/cli/cli_coap.cpp
+++ b/src/cli/cli_coap.cpp
@@ -44,8 +44,6 @@
 namespace ot {
 namespace Cli {
 
-constexpr Coap::Command Coap::sCommands[];
-
 Coap::Coap(Output &aOutput)
     : OutputWrapper(aOutput)
     , mUseDefaultRequestTxParameters(true)
@@ -144,7 +142,7 @@
 }
 
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
-otError Coap::ProcessCancel(Arg aArgs[])
+template <> otError Coap::Process<Cmd("cancel")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -152,19 +150,7 @@
 }
 #endif
 
-otError Coap::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
-otError Coap::ProcessResource(Arg aArgs[])
+template <> otError Coap::Process<Cmd("resource")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -203,7 +189,7 @@
     return error;
 }
 
-otError Coap::ProcessSet(Arg aArgs[])
+template <> otError Coap::Process<Cmd("set")>(Arg aArgs[])
 {
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
     otMessage *   notificationMessage = nullptr;
@@ -264,14 +250,14 @@
     return error;
 }
 
-otError Coap::ProcessStart(Arg aArgs[])
+template <> otError Coap::Process<Cmd("start")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
     return otCoapStart(GetInstancePtr(), OT_DEFAULT_COAP_PORT);
 }
 
-otError Coap::ProcessStop(Arg aArgs[])
+template <> otError Coap::Process<Cmd("stop")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -284,7 +270,7 @@
     return otCoapStop(GetInstancePtr());
 }
 
-otError Coap::ProcessParameters(Arg aArgs[])
+template <> otError Coap::Process<Cmd("parameters")>(Arg aArgs[])
 {
     otError             error = OT_ERROR_NONE;
     bool *              defaultTxParameters;
@@ -342,28 +328,28 @@
     return error;
 }
 
-otError Coap::ProcessGet(Arg aArgs[])
+template <> otError Coap::Process<Cmd("get")>(Arg aArgs[])
 {
     return ProcessRequest(aArgs, OT_COAP_CODE_GET);
 }
 
-otError Coap::ProcessPost(Arg aArgs[])
+template <> otError Coap::Process<Cmd("post")>(Arg aArgs[])
 {
     return ProcessRequest(aArgs, OT_COAP_CODE_POST);
 }
 
-otError Coap::ProcessPut(Arg aArgs[])
+template <> otError Coap::Process<Cmd("put")>(Arg aArgs[])
 {
     return ProcessRequest(aArgs, OT_COAP_CODE_PUT);
 }
 
-otError Coap::ProcessDelete(Arg aArgs[])
+template <> otError Coap::Process<Cmd("delete")>(Arg aArgs[])
 {
     return ProcessRequest(aArgs, OT_COAP_CODE_DELETE);
 }
 
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
-otError Coap::ProcessObserve(Arg aArgs[])
+template <> otError Coap::Process<Cmd("observe")>(Arg aArgs[])
 {
     return ProcessRequest(aArgs, OT_COAP_CODE_GET, /* aCoapObserve */ true);
 }
@@ -577,17 +563,44 @@
 
 otError Coap::Process(Arg aArgs[])
 {
-    otError        error = OT_ERROR_INVALID_ARGS;
-    const Command *command;
-
-    if (aArgs[0].IsEmpty())
-    {
-        IgnoreError(ProcessHelp(aArgs));
-        ExitNow();
+#define CmdEntry(aCommandString)                            \
+    {                                                       \
+        aCommandString, &Coap::Process<Cmd(aCommandString)> \
     }
 
-    command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
-    VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND);
+    static constexpr Command kCommands[] = {
+#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
+        CmdEntry("cancel"),
+#endif
+        CmdEntry("delete"),
+        CmdEntry("get"),
+#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
+        CmdEntry("observe"),
+#endif
+        CmdEntry("parameters"),
+        CmdEntry("post"),
+        CmdEntry("put"),
+        CmdEntry("resource"),
+        CmdEntry("set"),
+        CmdEntry("start"),
+        CmdEntry("stop"),
+    };
+
+#undef CmdEntry
+
+    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
+
+    otError        error = OT_ERROR_INVALID_COMMAND;
+    const Command *command;
+
+    if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
+    {
+        OutputCommandTable(kCommands);
+        ExitNow(error = aArgs[0].IsEmpty() ? OT_ERROR_INVALID_ARGS : OT_ERROR_NONE);
+    }
+
+    command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
+    VerifyOrExit(command != nullptr);
 
     error = (this->*command->mHandler)(aArgs + 1);
 
diff --git a/src/cli/cli_coap.hpp b/src/cli/cli_coap.hpp
index 022bff0..afa56bf 100644
--- a/src/cli/cli_coap.hpp
+++ b/src/cli/cli_coap.hpp
@@ -86,6 +86,8 @@
     };
 #endif
 
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
+
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
     otError CancelResourceSubscription(void);
     void    CancelSubscriber(void);
@@ -93,23 +95,6 @@
 
     void PrintPayload(otMessage *aMessage);
 
-    otError ProcessHelp(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
-    otError ProcessCancel(Arg aArgs[]);
-#endif
-    otError ProcessDelete(Arg aArgs[]);
-    otError ProcessGet(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
-    otError ProcessObserve(Arg aArgs[]);
-#endif
-    otError ProcessParameters(Arg aArgs[]);
-    otError ProcessPost(Arg aArgs[]);
-    otError ProcessPut(Arg aArgs[]);
-    otError ProcessResource(Arg aArgs[]);
-    otError ProcessSet(Arg aArgs[]);
-    otError ProcessStart(Arg aArgs[]);
-    otError ProcessStop(Arg aArgs[]);
-
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
     otError ProcessRequest(Arg aArgs[], otCoapCode aCoapCode, bool aCoapObserve = false);
 #else
@@ -161,27 +146,6 @@
         return mUseDefaultResponseTxParameters ? nullptr : &mResponseTxParameters;
     }
 
-    static constexpr Command sCommands[] = {
-#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
-        {"cancel", &Coap::ProcessCancel},
-#endif
-        {"delete", &Coap::ProcessDelete},
-        {"get", &Coap::ProcessGet},
-        {"help", &Coap::ProcessHelp},
-#if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
-        {"observe", &Coap::ProcessObserve},
-#endif
-        {"parameters", &Coap::ProcessParameters},
-        {"post", &Coap::ProcessPost},
-        {"put", &Coap::ProcessPut},
-        {"resource", &Coap::ProcessResource},
-        {"set", &Coap::ProcessSet},
-        {"start", &Coap::ProcessStart},
-        {"stop", &Coap::ProcessStop},
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
-
     bool mUseDefaultRequestTxParameters;
     bool mUseDefaultResponseTxParameters;
 
diff --git a/src/cli/cli_commissioner.cpp b/src/cli/cli_commissioner.cpp
index e8596fc..bbbcfce 100644
--- a/src/cli/cli_commissioner.cpp
+++ b/src/cli/cli_commissioner.cpp
@@ -40,21 +40,7 @@
 namespace ot {
 namespace Cli {
 
-constexpr Commissioner::Command Commissioner::sCommands[];
-
-otError Commissioner::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
-otError Commissioner::ProcessAnnounce(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("announce")>(Arg aArgs[])
 {
     otError      error;
     uint32_t     mask;
@@ -73,7 +59,7 @@
     return error;
 }
 
-otError Commissioner::ProcessEnergy(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("energy")>(Arg aArgs[])
 {
     otError      error;
     uint32_t     mask;
@@ -95,13 +81,54 @@
     return error;
 }
 
-otError Commissioner::ProcessJoiner(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("joiner")>(Arg aArgs[])
 {
     otError             error = OT_ERROR_NONE;
     otExtAddress        addr;
     const otExtAddress *addrPtr = nullptr;
     otJoinerDiscerner   discerner;
 
+    if (aArgs[0] == "table")
+    {
+        uint16_t     iter = 0;
+        otJoinerInfo joinerInfo;
+
+        static const char *const kJoinerTableTitles[] = {"ID", "PSKd", "Expiration"};
+
+        static const uint8_t kJoinerTableColumnWidths[] = {
+            23,
+            34,
+            12,
+        };
+
+        OutputTableHeader(kJoinerTableTitles, kJoinerTableColumnWidths);
+
+        while (otCommissionerGetNextJoinerInfo(GetInstancePtr(), &iter, &joinerInfo) == OT_ERROR_NONE)
+        {
+            switch (joinerInfo.mType)
+            {
+            case OT_JOINER_INFO_TYPE_ANY:
+                OutputFormat("| %21s", "*");
+                break;
+
+            case OT_JOINER_INFO_TYPE_EUI64:
+                OutputFormat("|      ");
+                OutputExtAddress(joinerInfo.mSharedId.mEui64);
+                break;
+
+            case OT_JOINER_INFO_TYPE_DISCERNER:
+                OutputFormat("| 0x%016llx/%2u", static_cast<unsigned long long>(joinerInfo.mSharedId.mDiscerner.mValue),
+                             joinerInfo.mSharedId.mDiscerner.mLength);
+                break;
+            }
+
+            OutputFormat(" | %32s | %10d |", joinerInfo.mPskd.m8, joinerInfo.mExpirationTime);
+            OutputLine("");
+        }
+
+        ExitNow(error = OT_ERROR_NONE);
+    }
+
     VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
 
     memset(&discerner, 0, sizeof(discerner));
@@ -163,7 +190,7 @@
     return error;
 }
 
-otError Commissioner::ProcessMgmtGet(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("mgmtget")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     uint8_t tlvs[32];
@@ -210,7 +237,7 @@
     return error;
 }
 
-otError Commissioner::ProcessMgmtSet(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("mgmtset")>(Arg aArgs[])
 {
     otError                error;
     otCommissioningDataset dataset;
@@ -272,7 +299,7 @@
     return error;
 }
 
-otError Commissioner::ProcessPanId(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("panid")>(Arg aArgs[])
 {
     otError      error;
     uint16_t     panId;
@@ -289,14 +316,14 @@
     return error;
 }
 
-otError Commissioner::ProcessProvisioningUrl(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("provisioningurl")>(Arg aArgs[])
 {
     // If aArgs[0] is empty, `GetCString() will return `nullptr`
     /// which will correctly clear the provisioning URL.
     return otCommissionerSetProvisioningUrl(GetInstancePtr(), aArgs[0].GetCString());
 }
 
-otError Commissioner::ProcessSessionId(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("sessionid")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -305,7 +332,24 @@
     return OT_ERROR_NONE;
 }
 
-otError Commissioner::ProcessStart(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("id")>(Arg aArgs[])
+{
+    otError error;
+
+    if (aArgs[0].IsEmpty())
+    {
+        OutputLine("%s", otCommissionerGetId(GetInstancePtr()));
+        error = OT_ERROR_NONE;
+    }
+    else
+    {
+        error = otCommissionerSetId(GetInstancePtr(), aArgs[0].GetCString());
+    }
+
+    return error;
+}
+
+template <> otError Commissioner::Process<Cmd("start")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -376,14 +420,14 @@
     OutputLine("");
 }
 
-otError Commissioner::ProcessStop(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("stop")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
     return otCommissionerStop(GetInstancePtr());
 }
 
-otError Commissioner::ProcessState(Arg aArgs[])
+template <> otError Commissioner::Process<Cmd("state")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -394,16 +438,31 @@
 
 otError Commissioner::Process(Arg aArgs[])
 {
+#define CmdEntry(aCommandString)                                    \
+    {                                                               \
+        aCommandString, &Commissioner::Process<Cmd(aCommandString)> \
+    }
+
+    static constexpr Command kCommands[] = {
+        CmdEntry("announce"),  CmdEntry("energy"),  CmdEntry("id"),    CmdEntry("joiner"),
+        CmdEntry("mgmtget"),   CmdEntry("mgmtset"), CmdEntry("panid"), CmdEntry("provisioningurl"),
+        CmdEntry("sessionid"), CmdEntry("start"),   CmdEntry("state"), CmdEntry("stop"),
+    };
+
+#undef CmdEntry
+
+    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
+
     otError        error = OT_ERROR_INVALID_COMMAND;
     const Command *command;
 
-    if (aArgs[0].IsEmpty())
+    if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
     {
-        IgnoreError(ProcessHelp(aArgs));
-        ExitNow();
+        OutputCommandTable(kCommands);
+        ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
     }
 
-    command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
+    command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
     VerifyOrExit(command != nullptr);
 
     error = (this->*command->mHandler)(aArgs + 1);
diff --git a/src/cli/cli_commissioner.hpp b/src/cli/cli_commissioner.hpp
index 7360818..d63089d 100644
--- a/src/cli/cli_commissioner.hpp
+++ b/src/cli/cli_commissioner.hpp
@@ -81,18 +81,7 @@
 
     using Command = CommandEntry<Commissioner>;
 
-    otError ProcessHelp(Arg aArgs[]);
-    otError ProcessAnnounce(Arg aArgs[]);
-    otError ProcessEnergy(Arg aArgs[]);
-    otError ProcessJoiner(Arg aArgs[]);
-    otError ProcessMgmtGet(Arg aArgs[]);
-    otError ProcessMgmtSet(Arg aArgs[]);
-    otError ProcessPanId(Arg aArgs[]);
-    otError ProcessProvisioningUrl(Arg aArgs[]);
-    otError ProcessSessionId(Arg aArgs[]);
-    otError ProcessStart(Arg aArgs[]);
-    otError ProcessState(Arg aArgs[]);
-    otError ProcessStop(Arg aArgs[]);
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
 
     static void HandleStateChanged(otCommissionerState aState, void *aContext);
     void        HandleStateChanged(otCommissionerState aState);
@@ -115,17 +104,6 @@
     void        HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask);
 
     static const char *StateToString(otCommissionerState aState);
-
-    static constexpr Command sCommands[] = {
-        {"announce", &Commissioner::ProcessAnnounce},   {"energy", &Commissioner::ProcessEnergy},
-        {"help", &Commissioner::ProcessHelp},           {"joiner", &Commissioner::ProcessJoiner},
-        {"mgmtget", &Commissioner::ProcessMgmtGet},     {"mgmtset", &Commissioner::ProcessMgmtSet},
-        {"panid", &Commissioner::ProcessPanId},         {"provisioningurl", &Commissioner::ProcessProvisioningUrl},
-        {"sessionid", &Commissioner::ProcessSessionId}, {"start", &Commissioner::ProcessStart},
-        {"state", &Commissioner::ProcessState},         {"stop", &Commissioner::ProcessStop},
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
 };
 
 } // namespace Cli
diff --git a/src/cli/cli_dataset.cpp b/src/cli/cli_dataset.cpp
index 61641a2..efade05 100644
--- a/src/cli/cli_dataset.cpp
+++ b/src/cli/cli_dataset.cpp
@@ -45,8 +45,7 @@
 namespace ot {
 namespace Cli {
 
-constexpr Dataset::Command Dataset::sCommands[];
-otOperationalDataset       Dataset::sDataset;
+otOperationalDataset Dataset::sDataset;
 
 otError Dataset::Print(otOperationalDataset &aDataset)
 {
@@ -119,38 +118,7 @@
     return OT_ERROR_NONE;
 }
 
-otError Dataset::Process(Arg aArgs[])
-{
-    otError        error = OT_ERROR_INVALID_COMMAND;
-    const Command *command;
-
-    if (aArgs[0].IsEmpty())
-    {
-        ExitNow(error = Print(sDataset));
-    }
-
-    command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
-    VerifyOrExit(command != nullptr);
-
-    error = (this->*command->mHandler)(aArgs + 1);
-
-exit:
-    return error;
-}
-
-otError Dataset::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
-otError Dataset::ProcessInit(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("init")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_ARGS;
 
@@ -183,7 +151,7 @@
     return error;
 }
 
-otError Dataset::ProcessActive(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("active")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_ARGS;
 
@@ -206,7 +174,7 @@
     return error;
 }
 
-otError Dataset::ProcessPending(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("pending")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_ARGS;
 
@@ -229,7 +197,7 @@
     return error;
 }
 
-otError Dataset::ProcessActiveTimestamp(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("activetimestamp")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -250,7 +218,7 @@
     return error;
 }
 
-otError Dataset::ProcessChannel(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("channel")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -271,7 +239,7 @@
     return error;
 }
 
-otError Dataset::ProcessChannelMask(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("channelmask")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -292,7 +260,7 @@
     return error;
 }
 
-otError Dataset::ProcessClear(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("clear")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -300,7 +268,7 @@
     return OT_ERROR_NONE;
 }
 
-otError Dataset::ProcessCommit(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("commit")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_ARGS;
 
@@ -316,7 +284,7 @@
     return error;
 }
 
-otError Dataset::ProcessDelay(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("delay")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -337,7 +305,7 @@
     return error;
 }
 
-otError Dataset::ProcessExtPanId(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("extpanid")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -358,7 +326,7 @@
     return error;
 }
 
-otError Dataset::ProcessMeshLocalPrefix(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("meshlocalprefix")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -384,7 +352,7 @@
     return error;
 }
 
-otError Dataset::ProcessNetworkKey(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("networkkey")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -405,7 +373,7 @@
     return error;
 }
 
-otError Dataset::ProcessNetworkName(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("networkname")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -426,7 +394,7 @@
     return error;
 }
 
-otError Dataset::ProcessPanId(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("panid")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -447,7 +415,7 @@
     return error;
 }
 
-otError Dataset::ProcessPendingTimestamp(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("pendingtimestamp")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -468,7 +436,7 @@
     return error;
 }
 
-otError Dataset::ProcessMgmtSetCommand(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("mgmtsetcommand")>(Arg aArgs[])
 {
     otError              error = OT_ERROR_NONE;
     otOperationalDataset dataset;
@@ -583,7 +551,7 @@
     return error;
 }
 
-otError Dataset::ProcessMgmtGetCommand(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("mgmtgetcommand")>(Arg aArgs[])
 {
     otError                        error = OT_ERROR_NONE;
     otOperationalDatasetComponents datasetComponents;
@@ -676,7 +644,7 @@
     return error;
 }
 
-otError Dataset::ProcessPskc(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("pskc")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -743,11 +711,6 @@
         OutputFormat("c");
     }
 
-    if (aSecurityPolicy.mBeaconsEnabled)
-    {
-        OutputFormat("b");
-    }
-
     if (aSecurityPolicy.mCommercialCommissioningEnabled)
     {
         OutputFormat("C");
@@ -803,10 +766,6 @@
             policy.mExternalCommissioningEnabled = true;
             break;
 
-        case 'b':
-            policy.mBeaconsEnabled = true;
-            break;
-
         case 'C':
             policy.mCommercialCommissioningEnabled = true;
             break;
@@ -839,7 +798,7 @@
     return error;
 }
 
-otError Dataset::ProcessSecurityPolicy(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("securitypolicy")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -862,7 +821,7 @@
     return error;
 }
 
-otError Dataset::ProcessSet(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("set")>(Arg aArgs[])
 {
     otError                error = OT_ERROR_NONE;
     MeshCoP::Dataset::Type datasetType;
@@ -907,7 +866,7 @@
 
 #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
 
-otError Dataset::ProcessUpdater(Arg aArgs[])
+template <> otError Dataset::Process<Cmd("updater")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -943,5 +902,65 @@
 
 #endif // OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
 
+otError Dataset::Process(Arg aArgs[])
+{
+#define CmdEntry(aCommandString)                               \
+    {                                                          \
+        aCommandString, &Dataset::Process<Cmd(aCommandString)> \
+    }
+
+    static constexpr Command kCommands[] = {
+        CmdEntry("active"),
+        CmdEntry("activetimestamp"),
+        CmdEntry("channel"),
+        CmdEntry("channelmask"),
+        CmdEntry("clear"),
+        CmdEntry("commit"),
+        CmdEntry("delay"),
+        CmdEntry("extpanid"),
+        CmdEntry("init"),
+        CmdEntry("meshlocalprefix"),
+        CmdEntry("mgmtgetcommand"),
+        CmdEntry("mgmtsetcommand"),
+        CmdEntry("networkkey"),
+        CmdEntry("networkname"),
+        CmdEntry("panid"),
+        CmdEntry("pending"),
+        CmdEntry("pendingtimestamp"),
+        CmdEntry("pskc"),
+        CmdEntry("securitypolicy"),
+        CmdEntry("set"),
+#if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
+        CmdEntry("updater"),
+#endif
+    };
+
+#undef CmdEntry
+
+    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
+
+    otError        error = OT_ERROR_INVALID_COMMAND;
+    const Command *command;
+
+    if (aArgs[0].IsEmpty())
+    {
+        ExitNow(error = Print(sDataset));
+    }
+
+    if (aArgs[0] == "help")
+    {
+        OutputCommandTable(kCommands);
+        ExitNow(error = OT_ERROR_NONE);
+    }
+
+    command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
+    VerifyOrExit(command != nullptr);
+
+    error = (this->*command->mHandler)(aArgs + 1);
+
+exit:
+    return error;
+}
+
 } // namespace Cli
 } // namespace ot
diff --git a/src/cli/cli_dataset.hpp b/src/cli/cli_dataset.hpp
index d32afdd..980bb5d 100644
--- a/src/cli/cli_dataset.hpp
+++ b/src/cli/cli_dataset.hpp
@@ -70,29 +70,9 @@
 private:
     using Command = CommandEntry<Dataset>;
 
-    otError Print(otOperationalDataset &aDataset);
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
 
-    otError ProcessHelp(Arg aArgs[]);
-    otError ProcessActive(Arg aArgs[]);
-    otError ProcessActiveTimestamp(Arg aArgs[]);
-    otError ProcessChannel(Arg aArgs[]);
-    otError ProcessChannelMask(Arg aArgs[]);
-    otError ProcessClear(Arg aArgs[]);
-    otError ProcessCommit(Arg aArgs[]);
-    otError ProcessDelay(Arg aArgs[]);
-    otError ProcessExtPanId(Arg aArgs[]);
-    otError ProcessInit(Arg aArgs[]);
-    otError ProcessMeshLocalPrefix(Arg aArgs[]);
-    otError ProcessNetworkName(Arg aArgs[]);
-    otError ProcessNetworkKey(Arg aArgs[]);
-    otError ProcessPanId(Arg aArgs[]);
-    otError ProcessPending(Arg aArgs[]);
-    otError ProcessPendingTimestamp(Arg aArgs[]);
-    otError ProcessMgmtSetCommand(Arg aArgs[]);
-    otError ProcessMgmtGetCommand(Arg aArgs[]);
-    otError ProcessPskc(Arg aArgs[]);
-    otError ProcessSecurityPolicy(Arg aArgs[]);
-    otError ProcessSet(Arg aArgs[]);
+    otError Print(otOperationalDataset &aDataset);
 
 #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
     otError     ProcessUpdater(Arg aArgs[]);
@@ -103,35 +83,6 @@
     void    OutputSecurityPolicy(const otSecurityPolicy &aSecurityPolicy);
     otError ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, Arg *&aArgs);
 
-    static constexpr Command sCommands[] = {
-        {"active", &Dataset::ProcessActive},
-        {"activetimestamp", &Dataset::ProcessActiveTimestamp},
-        {"channel", &Dataset::ProcessChannel},
-        {"channelmask", &Dataset::ProcessChannelMask},
-        {"clear", &Dataset::ProcessClear},
-        {"commit", &Dataset::ProcessCommit},
-        {"delay", &Dataset::ProcessDelay},
-        {"extpanid", &Dataset::ProcessExtPanId},
-        {"help", &Dataset::ProcessHelp},
-        {"init", &Dataset::ProcessInit},
-        {"meshlocalprefix", &Dataset::ProcessMeshLocalPrefix},
-        {"mgmtgetcommand", &Dataset::ProcessMgmtGetCommand},
-        {"mgmtsetcommand", &Dataset::ProcessMgmtSetCommand},
-        {"networkkey", &Dataset::ProcessNetworkKey},
-        {"networkname", &Dataset::ProcessNetworkName},
-        {"panid", &Dataset::ProcessPanId},
-        {"pending", &Dataset::ProcessPending},
-        {"pendingtimestamp", &Dataset::ProcessPendingTimestamp},
-        {"pskc", &Dataset::ProcessPskc},
-        {"securitypolicy", &Dataset::ProcessSecurityPolicy},
-        {"set", &Dataset::ProcessSet},
-#if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD
-        {"updater", &Dataset::ProcessUpdater},
-#endif
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
-
     static otOperationalDataset sDataset;
 };
 
diff --git a/src/cli/cli_history.cpp b/src/cli/cli_history.cpp
index 422ee61..5f6095c 100644
--- a/src/cli/cli_history.cpp
+++ b/src/cli/cli_history.cpp
@@ -42,25 +42,11 @@
 namespace ot {
 namespace Cli {
 
-constexpr History::Command History::sCommands[];
-
 static const char *const kSimpleEventStrings[] = {
     "Added",  // (0) OT_HISTORY_TRACKER_{NET_DATA_ENTRY/ADDRESSS_EVENT}_ADDED
     "Removed" // (1) OT_HISTORY_TRACKER_{NET_DATA_ENTRY/ADDRESS_EVENT}_REMOVED
 };
 
-otError History::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
 otError History::ParseArgs(Arg aArgs[], bool &aIsList, uint16_t &aNumEntries) const
 {
     if (*aArgs == "list")
@@ -85,7 +71,7 @@
     return aArgs[0].IsEmpty() ? OT_ERROR_NONE : OT_ERROR_INVALID_ARGS;
 }
 
-otError History::ProcessIpAddr(Arg aArgs[])
+template <> otError History::Process<Cmd("ipaddr")>(Arg aArgs[])
 {
     otError                                   error;
     bool                                      isList;
@@ -146,7 +132,7 @@
     return error;
 }
 
-otError History::ProcessIpMulticastAddr(Arg aArgs[])
+template <> otError History::Process<Cmd("ipmaddr")>(Arg aArgs[])
 {
     static const char *const kEventStrings[] = {
         "Subscribed",  // (0) OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED
@@ -203,7 +189,7 @@
     return error;
 }
 
-otError History::ProcessNeighbor(Arg aArgs[])
+template <> otError History::Process<Cmd("neighbor")>(Arg aArgs[])
 {
     static const char *const kEventString[] = {
         /* (0) OT_HISTORY_TRACKER_NEIGHBOR_EVENT_ADDED     -> */ "Added",
@@ -268,7 +254,7 @@
     return error;
 }
 
-otError History::ProcessNetInfo(Arg aArgs[])
+template <> otError History::Process<Cmd("netinfo")>(Arg aArgs[])
 {
     otError                            error;
     bool                               isList;
@@ -311,17 +297,17 @@
     return error;
 }
 
-otError History::ProcessRx(Arg aArgs[])
+template <> otError History::Process<Cmd("rx")>(Arg aArgs[])
 {
     return ProcessRxTxHistory(kRx, aArgs);
 }
 
-otError History::ProcessRxTx(Arg aArgs[])
+template <> otError History::Process<Cmd("rxtx")>(Arg aArgs[])
 {
     return ProcessRxTxHistory(kRxTx, aArgs);
 }
 
-otError History::ProcessTx(Arg aArgs[])
+template <> otError History::Process<Cmd("tx")>(Arg aArgs[])
 {
     return ProcessRxTxHistory(kTx, aArgs);
 }
@@ -571,7 +557,7 @@
     OutputLine("| %20s | dst: %-70s |", "", addrString);
 }
 
-otError History::ProcessPrefix(Arg aArgs[])
+template <> otError History::Process<Cmd("prefix")>(Arg aArgs[])
 {
     otError                                 error;
     bool                                    isList;
@@ -621,7 +607,7 @@
     return error;
 }
 
-otError History::ProcessRoute(Arg aArgs[])
+template <> otError History::Process<Cmd("route")>(Arg aArgs[])
 {
     otError                                  error;
     bool                                     isList;
@@ -673,16 +659,30 @@
 
 otError History::Process(Arg aArgs[])
 {
+#define CmdEntry(aCommandString)                               \
+    {                                                          \
+        aCommandString, &History::Process<Cmd(aCommandString)> \
+    }
+
+    static constexpr Command kCommands[] = {
+        CmdEntry("ipaddr"), CmdEntry("ipmaddr"), CmdEntry("neighbor"), CmdEntry("netinfo"), CmdEntry("prefix"),
+        CmdEntry("route"),  CmdEntry("rx"),      CmdEntry("rxtx"),     CmdEntry("tx"),
+    };
+
+#undef CmdEntry
+
+    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
+
     otError        error = OT_ERROR_INVALID_COMMAND;
     const Command *command;
 
-    if (aArgs[0].IsEmpty())
+    if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
     {
-        IgnoreError(ProcessHelp(aArgs));
-        ExitNow();
+        OutputCommandTable(kCommands);
+        ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
     }
 
-    command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
+    command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
     VerifyOrExit(command != nullptr);
 
     error = (this->*command->mHandler)(aArgs + 1);
diff --git a/src/cli/cli_history.hpp b/src/cli/cli_history.hpp
index b39a877..a17a667 100644
--- a/src/cli/cli_history.hpp
+++ b/src/cli/cli_history.hpp
@@ -88,16 +88,7 @@
         kRxTx,
     };
 
-    otError ProcessHelp(Arg aArgs[]);
-    otError ProcessIpAddr(Arg aArgs[]);
-    otError ProcessIpMulticastAddr(Arg aArgs[]);
-    otError ProcessNetInfo(Arg aArgs[]);
-    otError ProcessNeighbor(Arg aArgs[]);
-    otError ProcessPrefix(Arg aArgs[]);
-    otError ProcessRoute(Arg aArgs[]);
-    otError ProcessRx(Arg aArgs[]);
-    otError ProcessRxTx(Arg aArgs[]);
-    otError ProcessTx(Arg aArgs[]);
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
 
     otError ParseArgs(Arg aArgs[], bool &aIsList, uint16_t &aNumEntries) const;
     otError ProcessRxTxHistory(RxTx aRxTx, Arg aArgs[]);
@@ -107,21 +98,6 @@
     static const char *MessagePriorityToString(uint8_t aPriority);
     static const char *RadioTypeToString(const otHistoryTrackerMessageInfo &aInfo);
     static const char *MessageTypeToString(const otHistoryTrackerMessageInfo &aInfo);
-
-    static constexpr Command sCommands[] = {
-        {"help", &History::ProcessHelp},
-        {"ipaddr", &History::ProcessIpAddr},
-        {"ipmaddr", &History::ProcessIpMulticastAddr},
-        {"neighbor", &History::ProcessNeighbor},
-        {"netinfo", &History::ProcessNetInfo},
-        {"prefix", &History::ProcessPrefix},
-        {"route", &History::ProcessRoute},
-        {"rx", &History::ProcessRx},
-        {"rxtx", &History::ProcessRxTx},
-        {"tx", &History::ProcessTx},
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
 };
 
 } // namespace Cli
diff --git a/src/cli/cli_joiner.cpp b/src/cli/cli_joiner.cpp
index a4c6e73..c4509fe 100644
--- a/src/cli/cli_joiner.cpp
+++ b/src/cli/cli_joiner.cpp
@@ -42,9 +42,7 @@
 namespace ot {
 namespace Cli {
 
-constexpr Joiner::Command Joiner::sCommands[];
-
-otError Joiner::ProcessDiscerner(Arg aArgs[])
+template <> otError Joiner::Process<Cmd("discerner")>(Arg aArgs[])
 {
     otError error = OT_ERROR_INVALID_ARGS;
 
@@ -79,19 +77,7 @@
     return error;
 }
 
-otError Joiner::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
-otError Joiner::ProcessId(Arg aArgs[])
+template <> otError Joiner::Process<Cmd("id")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -100,7 +86,7 @@
     return OT_ERROR_NONE;
 }
 
-otError Joiner::ProcessStart(Arg aArgs[])
+template <> otError Joiner::Process<Cmd("start")>(Arg aArgs[])
 {
     otError error;
 
@@ -119,7 +105,7 @@
     return error;
 }
 
-otError Joiner::ProcessStop(Arg aArgs[])
+template <> otError Joiner::Process<Cmd("stop")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -128,7 +114,7 @@
     return OT_ERROR_NONE;
 }
 
-otError Joiner::ProcessState(Arg aArgs[])
+template <> otError Joiner::Process<Cmd("state")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -139,16 +125,29 @@
 
 otError Joiner::Process(Arg aArgs[])
 {
+#define CmdEntry(aCommandString)                              \
+    {                                                         \
+        aCommandString, &Joiner::Process<Cmd(aCommandString)> \
+    }
+
+    static constexpr Command kCommands[] = {
+        CmdEntry("discerner"), CmdEntry("id"), CmdEntry("start"), CmdEntry("state"), CmdEntry("stop"),
+    };
+
+#undef CmdEntry
+
+    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
+
     otError        error = OT_ERROR_INVALID_COMMAND;
     const Command *command;
 
-    if (aArgs[0].IsEmpty())
+    if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
     {
-        IgnoreError(ProcessHelp(aArgs));
-        ExitNow();
+        OutputCommandTable(kCommands);
+        ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
     }
 
-    command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
+    command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
     VerifyOrExit(command != nullptr);
 
     error = (this->*command->mHandler)(aArgs + 1);
diff --git a/src/cli/cli_joiner.hpp b/src/cli/cli_joiner.hpp
index f86a62d..d2bc640 100644
--- a/src/cli/cli_joiner.hpp
+++ b/src/cli/cli_joiner.hpp
@@ -76,22 +76,10 @@
 private:
     using Command = CommandEntry<Joiner>;
 
-    otError ProcessDiscerner(Arg aArgs[]);
-    otError ProcessHelp(Arg aArgs[]);
-    otError ProcessId(Arg aArgs[]);
-    otError ProcessStart(Arg aArgs[]);
-    otError ProcessStop(Arg aArgs[]);
-    otError ProcessState(Arg aArgs[]);
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
 
     static void HandleCallback(otError aError, void *aContext);
     void        HandleCallback(otError aError);
-
-    static constexpr Command sCommands[] = {
-        {"discerner", &Joiner::ProcessDiscerner}, {"help", &Joiner::ProcessHelp},   {"id", &Joiner::ProcessId},
-        {"start", &Joiner::ProcessStart},         {"state", &Joiner::ProcessState}, {"stop", &Joiner::ProcessStop},
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
 };
 
 } // namespace Cli
diff --git a/src/cli/cli_network_data.cpp b/src/cli/cli_network_data.cpp
index 54a2239..ee02b1b 100644
--- a/src/cli/cli_network_data.cpp
+++ b/src/cli/cli_network_data.cpp
@@ -43,8 +43,6 @@
 namespace ot {
 namespace Cli {
 
-constexpr NetworkData::Command NetworkData::sCommands[];
-
 void NetworkData::PrefixFlagsToString(const otBorderRouterConfig &aConfig, FlagsString &aString)
 {
     char *flagsPtr = &aString[0];
@@ -186,20 +184,8 @@
     OutputLine(" %04x", aConfig.mServerConfig.mRloc16);
 }
 
-otError NetworkData::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
 #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
-otError NetworkData::ProcessPublish(Arg aArgs[])
+template <> otError NetworkData::Process<Cmd("publish")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -261,7 +247,7 @@
     return error;
 }
 
-otError NetworkData::ProcessUnpublish(Arg aArgs[])
+template <> otError NetworkData::Process<Cmd("unpublish")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -293,7 +279,7 @@
 #endif // OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
-otError NetworkData::ProcessRegister(Arg aArgs[])
+template <> otError NetworkData::Process<Cmd("register")>(Arg aArgs[])
 {
     OT_UNUSED_VARIABLE(aArgs);
 
@@ -310,7 +296,7 @@
 }
 #endif
 
-otError NetworkData::ProcessSteeringData(Arg aArgs[])
+template <> otError NetworkData::Process<Cmd("steeringdata")>(Arg aArgs[])
 {
     otError           error;
     otExtAddress      addr;
@@ -466,7 +452,7 @@
     return error;
 }
 
-otError NetworkData::ProcessShow(Arg aArgs[])
+template <> otError NetworkData::Process<Cmd("show")>(Arg aArgs[])
 {
     otError error  = OT_ERROR_INVALID_ARGS;
     bool    local  = false;
@@ -506,16 +492,39 @@
 
 otError NetworkData::Process(Arg aArgs[])
 {
+#define CmdEntry(aCommandString)                                   \
+    {                                                              \
+        aCommandString, &NetworkData::Process<Cmd(aCommandString)> \
+    }
+
+    static constexpr Command kCommands[] = {
+#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
+        CmdEntry("publish"),
+#endif
+#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
+        CmdEntry("register"),
+#endif
+        CmdEntry("show"),
+        CmdEntry("steeringdata"),
+#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
+        CmdEntry("unpublish"),
+#endif
+    };
+
+#undef CmdEntry
+
+    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
+
     otError        error = OT_ERROR_INVALID_COMMAND;
     const Command *command;
 
-    if (aArgs[0].IsEmpty())
+    if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
     {
-        IgnoreError(ProcessHelp(aArgs));
-        ExitNow();
+        OutputCommandTable(kCommands);
+        ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
     }
 
-    command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
+    command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
     VerifyOrExit(command != nullptr);
 
     error = (this->*command->mHandler)(aArgs + 1);
diff --git a/src/cli/cli_network_data.hpp b/src/cli/cli_network_data.hpp
index 2efc3a7..0fb2098 100644
--- a/src/cli/cli_network_data.hpp
+++ b/src/cli/cli_network_data.hpp
@@ -138,16 +138,7 @@
 private:
     using Command = CommandEntry<NetworkData>;
 
-    otError ProcessHelp(Arg aArgs[]);
-#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
-    otError ProcessPublish(Arg aArgs[]);
-    otError ProcessUnpublish(Arg aArgs[]);
-#endif
-#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
-    otError ProcessRegister(Arg aArgs[]);
-#endif
-    otError ProcessShow(Arg aArgs[]);
-    otError ProcessSteeringData(Arg aArgs[]);
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
 
     otError GetNextPrefix(otNetworkDataIterator *aIterator, otBorderRouterConfig *aConfig, bool aLocal);
     otError GetNextRoute(otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig, bool aLocal);
@@ -157,23 +148,6 @@
     void    OutputPrefixes(bool aLocal);
     void    OutputRoutes(bool aLocal);
     void    OutputServices(bool aLocal);
-
-    static constexpr Command sCommands[] = {
-        {"help", &NetworkData::ProcessHelp},
-#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
-        {"publish", &NetworkData::ProcessPublish},
-#endif
-#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
-        {"register", &NetworkData::ProcessRegister},
-#endif
-        {"show", &NetworkData::ProcessShow},
-        {"steeringdata", &NetworkData::ProcessSteeringData},
-#if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE
-        {"unpublish", &NetworkData::ProcessUnpublish},
-#endif
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
 };
 
 } // namespace Cli
diff --git a/src/cli/cli_output.cpp b/src/cli/cli_output.cpp
index af712af..7d2208e 100644
--- a/src/cli/cli_output.cpp
+++ b/src/cli/cli_output.cpp
@@ -40,8 +40,8 @@
 #if OPENTHREAD_FTD || OPENTHREAD_MTD
 #include <openthread/dns.h>
 #endif
+#include <openthread/logging.h>
 
-#include "common/logging.hpp"
 #include "common/string.hpp"
 
 namespace ot {
@@ -285,7 +285,7 @@
 
         if (lineEnd > mOutputString)
         {
-            otLogNoteCli("Output: %s", mOutputString);
+            otLogCli(OT_LOG_LEVEL_DEBG, "Output: %s", mOutputString);
         }
 
         lineEnd++;
@@ -326,7 +326,7 @@
 
     if (truncated)
     {
-        otLogNoteCli("Output: %s ...", mOutputString);
+        otLogCli(OT_LOG_LEVEL_DEBG, "Output: %s ...", mOutputString);
         mOutputLength = 0;
     }
 
@@ -345,7 +345,7 @@
         inputString.Append(isFirst ? "%s" : " %s", aArgs->GetCString());
     }
 
-    otLogNoteCli("Input: %s", inputString.AsCString());
+    otLogCli(OT_LOG_LEVEL_DEBG, "Input: %s", inputString.AsCString());
 }
 #endif
 
diff --git a/src/cli/cli_output.hpp b/src/cli/cli_output.hpp
index a7aa402..61e8997 100644
--- a/src/cli/cli_output.hpp
+++ b/src/cli/cli_output.hpp
@@ -50,12 +50,73 @@
 namespace Cli {
 
 /**
+ * This type represents a ID number value associated with a CLI command string.
+ *
+ */
+typedef uint64_t CommandId;
+
+/**
+ * This `constexpr` function converts a CLI command string to its associated `CommandId` value.
+ *
+ * @param[in] aString   The CLI command string.
+ *
+ * @returns The associated `CommandId` with @p aString.
+ *
+ */
+constexpr static CommandId Cmd(const char *aString)
+{
+    return (aString[0] == '\0') ? 0 : (static_cast<uint8_t>(aString[0]) + Cmd(aString + 1) * 255u);
+}
+
+/**
  * This class is the base class for `Output` and `OutputWrapper` providing common helper methods.
  *
  */
 class OutputBase
 {
 public:
+    typedef Utils::CmdLineParser::Arg Arg; ///< An argument
+
+    /**
+     * This structure represent a CLI command table entry, mapping a command with `aName` to a handler method.
+     *
+     * @tparam Cli    The CLI module type.
+     *
+     */
+    template <typename Cli> struct CommandEntry
+    {
+        typedef otError (Cli::*Handler)(Arg aArgs[]); ///< The handler method pointer type.
+
+        /**
+         * This method compares the entry's name with a given name.
+         *
+         * @param aName    The name string to compare with.
+         *
+         * @return zero means perfect match, positive (> 0) indicates @p aName is larger than entry's name, and
+         *         negative (< 0) indicates @p aName is smaller than entry's name.
+         *
+         */
+        int Compare(const char *aName) const { return strcmp(aName, mName); }
+
+        /**
+         * This `constexpr` method compares two entries to check if they are in order.
+         *
+         * @param[in] aFirst     The first entry.
+         * @param[in] aSecond    The second entry.
+         *
+         * @retval TRUE  if @p aFirst and @p aSecond are in order, i.e. `aFirst < aSecond`.
+         * @retval FALSE if @p aFirst and @p aSecond are not in order, i.e. `aFirst >= aSecond`.
+         *
+         */
+        constexpr static bool AreInOrder(const CommandEntry &aFirst, const CommandEntry &aSecond)
+        {
+            return AreStringsInOrder(aFirst.mName, aSecond.mName);
+        }
+
+        const char *mName;    ///< The command name.
+        Handler     mHandler; ///< The handler method pointer.
+    };
+
     static const char kUnknownString[]; // Constant string "unknown".
 
     /**
@@ -82,23 +143,6 @@
 
 protected:
     OutputBase(void) = default;
-
-    typedef Utils::CmdLineParser::Arg Arg;
-
-    template <typename Cli> struct CommandEntry
-    {
-        typedef otError (Cli::*Handler)(Arg aArgs[]);
-
-        int Compare(const char *aName) const { return strcmp(aName, mName); }
-
-        constexpr static bool AreInOrder(const CommandEntry &aFirst, const CommandEntry &aSecond)
-        {
-            return AreStringsInOrder(aFirst.mName, aSecond.mName);
-        }
-
-        const char *mName;
-        Handler     mHandler;
-    };
 };
 
 /**
@@ -335,7 +379,7 @@
      * @tparam kTableNumColumns   The number columns in the table.
      *
      * @param[in] aTitles   An array specifying the table column titles.
-     * @param[in] aWidth    An array specifying the table column widths (in number of chars).
+     * @param[in] aWidths   An array specifying the table column widths (in number of chars).
      *
      */
     template <uint8_t kTableNumColumns>
@@ -355,7 +399,7 @@
      *
      * @tparam kTableNumColumns   The number columns in the table.
      *
-     * @param[in] aWidth    An array specifying the table column widths (in number of chars).
+     * @param[in] aWidths   An array specifying the table column widths (in number of chars).
      *
      */
     template <uint8_t kTableNumColumns> void OutputTableSeparator(const uint8_t (&aWidths)[kTableNumColumns])
@@ -363,6 +407,23 @@
         OutputTableSeparator(kTableNumColumns, &aWidths[0]);
     }
 
+    /**
+     * This method outputs the list of commands from a given command table.
+     *
+     * @tparam Cli      The CLI module type.
+     * @tparam kLength  The length of command table array.
+     *
+     * @param[in] aCommandTable   The command table array.
+     *
+     */
+    template <typename Cli, uint16_t kLength> void OutputCommandTable(const CommandEntry<Cli> (&aCommandTable)[kLength])
+    {
+        for (const CommandEntry<Cli> &entry : aCommandTable)
+        {
+            OutputLine("%s", entry.mName);
+        }
+    }
+
 protected:
     void OutputFormatV(const char *aFormat, va_list aArguments);
 
@@ -463,6 +524,11 @@
         mOutput.OutputTableSeparator(aWidths);
     }
 
+    template <typename Cli, uint16_t kLength> void OutputCommandTable(const CommandEntry<Cli> (&aCommandTable)[kLength])
+    {
+        mOutput.OutputCommandTable(aCommandTable);
+    }
+
 private:
     Output &mOutput;
 };
diff --git a/src/cli/cli_srp_client.cpp b/src/cli/cli_srp_client.cpp
index 7ffa91c..fa1b740 100644
--- a/src/cli/cli_srp_client.cpp
+++ b/src/cli/cli_srp_client.cpp
@@ -42,8 +42,6 @@
 namespace ot {
 namespace Cli {
 
-constexpr SrpClient::Command SrpClient::sCommands[];
-
 static otError CopyString(char *aDest, uint16_t aDestSize, const char *aSource)
 {
     // Copies a string from `aSource` to `aDestination` (char array),
@@ -66,29 +64,9 @@
     otSrpClientSetCallback(GetInstancePtr(), SrpClient::HandleCallback, this);
 }
 
-otError SrpClient::Process(Arg aArgs[])
-{
-    otError        error = OT_ERROR_INVALID_COMMAND;
-    const Command *command;
-
-    if (aArgs[0].IsEmpty())
-    {
-        IgnoreError(ProcessHelp(aArgs));
-        ExitNow();
-    }
-
-    command = BinarySearch::Find(aArgs[0].GetCString(), sCommands);
-    VerifyOrExit(command != nullptr);
-
-    error = (this->*command->mHandler)(aArgs + 1);
-
-exit:
-    return error;
-}
-
 #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
 
-otError SrpClient::ProcessAutoStart(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("autostart")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     bool    enable;
@@ -116,7 +94,7 @@
 
 #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE
 
-otError SrpClient::ProcessCallback(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("callback")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -132,19 +110,7 @@
     return error;
 }
 
-otError SrpClient::ProcessHelp(Arg aArgs[])
-{
-    OT_UNUSED_VARIABLE(aArgs);
-
-    for (const Command &command : sCommands)
-    {
-        OutputLine(command.mName);
-    }
-
-    return OT_ERROR_NONE;
-}
-
-otError SrpClient::ProcessHost(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("host")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -265,18 +231,18 @@
     return error;
 }
 
-otError SrpClient::ProcessLeaseInterval(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("leaseinterval")>(Arg aArgs[])
 {
     return Interpreter::GetInterpreter().ProcessGetSet(aArgs, otSrpClientGetLeaseInterval, otSrpClientSetLeaseInterval);
 }
 
-otError SrpClient::ProcessKeyLeaseInterval(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("keyleaseinterval")>(Arg aArgs[])
 {
     return Interpreter::GetInterpreter().ProcessGetSet(aArgs, otSrpClientGetKeyLeaseInterval,
                                                        otSrpClientSetKeyLeaseInterval);
 }
 
-otError SrpClient::ProcessServer(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("server")>(Arg aArgs[])
 {
     otError           error          = OT_ERROR_NONE;
     const otSockAddr *serverSockAddr = otSrpClientGetServerAddress(GetInstancePtr());
@@ -306,7 +272,7 @@
     return error;
 }
 
-otError SrpClient::ProcessService(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("service")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
     bool    isRemove;
@@ -521,7 +487,7 @@
                aService.mPort, aService.mPriority, aService.mWeight);
 }
 
-otError SrpClient::ProcessStart(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("start")>(Arg aArgs[])
 {
     otError    error = OT_ERROR_NONE;
     otSockAddr serverSockAddr;
@@ -536,7 +502,7 @@
     return error;
 }
 
-otError SrpClient::ProcessState(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("state")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -548,7 +514,7 @@
     return error;
 }
 
-otError SrpClient::ProcessStop(Arg aArgs[])
+template <> otError SrpClient::Process<Cmd("stop")>(Arg aArgs[])
 {
     otError error = OT_ERROR_NONE;
 
@@ -602,6 +568,39 @@
     }
 }
 
+otError SrpClient::Process(Arg aArgs[])
+{
+#define CmdEntry(aCommandString)                                 \
+    {                                                            \
+        aCommandString, &SrpClient::Process<Cmd(aCommandString)> \
+    }
+
+    static constexpr Command kCommands[] = {
+        CmdEntry("autostart"),     CmdEntry("callback"), CmdEntry("host"),    CmdEntry("keyleaseinterval"),
+        CmdEntry("leaseinterval"), CmdEntry("server"),   CmdEntry("service"), CmdEntry("start"),
+        CmdEntry("state"),         CmdEntry("stop"),
+    };
+
+    static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
+
+    otError        error = OT_ERROR_INVALID_COMMAND;
+    const Command *command;
+
+    if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
+    {
+        OutputCommandTable(kCommands);
+        ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
+    }
+
+    command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
+    VerifyOrExit(command != nullptr);
+
+    error = (this->*command->mHandler)(aArgs + 1);
+
+exit:
+    return error;
+}
+
 } // namespace Cli
 } // namespace ot
 
diff --git a/src/cli/cli_srp_client.hpp b/src/cli/cli_srp_client.hpp
index 5f73ae6..b868e6f 100644
--- a/src/cli/cli_srp_client.hpp
+++ b/src/cli/cli_srp_client.hpp
@@ -81,18 +81,9 @@
 
     using Command = CommandEntry<SrpClient>;
 
-    otError ProcessAutoStart(Arg aArgs[]);
-    otError ProcessCallback(Arg aArgs[]);
-    otError ProcessHelp(Arg aArgs[]);
-    otError ProcessHost(Arg aArgs[]);
-    otError ProcessLeaseInterval(Arg aArgs[]);
-    otError ProcessKeyLeaseInterval(Arg aArgs[]);
-    otError ProcessServer(Arg aArgs[]);
-    otError ProcessService(Arg aArgs[]);
+    template <CommandId kCommandId> otError Process(Arg aArgs[]);
+
     otError ProcessServiceAdd(Arg aArgs[]);
-    otError ProcessStart(Arg aArgs[]);
-    otError ProcessState(Arg aArgs[]);
-    otError ProcessStop(Arg aArgs[]);
 
     void OutputHostInfo(uint8_t aIndentSize, const otSrpClientHostInfo &aHostInfo);
     void OutputServiceList(uint8_t aIndentSize, const otSrpClientService *aServices);
@@ -108,22 +99,6 @@
                                const otSrpClientService * aServices,
                                const otSrpClientService * aRemovedServices);
 
-    static constexpr Command sCommands[] = {
-        {"autostart", &SrpClient::ProcessAutoStart},
-        {"callback", &SrpClient::ProcessCallback},
-        {"help", &SrpClient::ProcessHelp},
-        {"host", &SrpClient::ProcessHost},
-        {"keyleaseinterval", &SrpClient::ProcessKeyLeaseInterval},
-        {"leaseinterval", &SrpClient::ProcessLeaseInterval},
-        {"server", &SrpClient::ProcessServer},
-        {"service", &SrpClient::ProcessService},
-        {"start", &SrpClient::ProcessStart},
-        {"state", &SrpClient::ProcessState},
-        {"stop", &SrpClient::ProcessStop},
-    };
-
-    static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted");
-
     bool mCallbackEnabled;
 };
 
diff --git a/src/cli/cli_udp.hpp b/src/cli/cli_udp.hpp
index 11dfc57..bd8ba54 100644
--- a/src/cli/cli_udp.hpp
+++ b/src/cli/cli_udp.hpp
@@ -55,7 +55,7 @@
     /**
      * Constructor
      *
-     * @param[in]  aOutputContext The CLI console output context.
+     * @param[in]  aOutput The CLI console output context.
      *
      */
     explicit UdpExample(Output &aOutput);
diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn
index 973c76b..5ba6b7c 100644
--- a/src/core/BUILD.gn
+++ b/src/core/BUILD.gn
@@ -261,12 +261,15 @@
     if (openthread_config_full_logs) {
       defines += [ "OPENTHREAD_CONFIG_LOG_LEVEL=OT_LOG_LEVEL_DEBG" ]
       defines += [ "OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL=1" ]
-      defines += [ "OPENTHREAD_CONFIG_LOG_PREPEND_REGION=1" ]
     }
 
     if (openthread_config_otns_enable) {
       defines += [ "OPENTHREAD_CONFIG_OTNS_ENABLE=1" ]
     }
+
+    if (openthread_config_coexistence_enable) {
+      defines += [ "OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE=1" ]
+    }
   }
 }
 
@@ -398,6 +401,7 @@
   "common/heap.cpp",
   "common/heap.hpp",
   "common/heap_allocatable.hpp",
+  "common/heap_array.hpp",
   "common/heap_data.cpp",
   "common/heap_data.hpp",
   "common/heap_string.cpp",
@@ -408,7 +412,8 @@
   "common/linked_list.hpp",
   "common/locator.hpp",
   "common/locator_getters.hpp",
-  "common/logging.cpp",
+  "common/log.cpp",
+  "common/log.hpp",
   "common/logging.hpp",
   "common/message.cpp",
   "common/message.hpp",
@@ -421,9 +426,8 @@
   "common/owning_list.hpp",
   "common/pool.hpp",
   "common/ptr_wrapper.hpp",
+  "common/random.cpp",
   "common/random.hpp",
-  "common/random_manager.cpp",
-  "common/random_manager.hpp",
   "common/retain_ptr.hpp",
   "common/serial_number.hpp",
   "common/settings.cpp",
@@ -453,6 +457,7 @@
   "crypto/crypto_platform.cpp",
   "crypto/ecdsa.cpp",
   "crypto/ecdsa.hpp",
+  "crypto/ecdsa_tinycrypt.cpp",
   "crypto/hkdf_sha256.cpp",
   "crypto/hkdf_sha256.hpp",
   "crypto/hmac_sha256.cpp",
@@ -507,6 +512,8 @@
   "meshcop/dtls.hpp",
   "meshcop/energy_scan_client.cpp",
   "meshcop/energy_scan_client.hpp",
+  "meshcop/extended_panid.cpp",
+  "meshcop/extended_panid.hpp",
   "meshcop/joiner.cpp",
   "meshcop/joiner.hpp",
   "meshcop/joiner_router.cpp",
@@ -517,6 +524,8 @@
   "meshcop/meshcop_leader.hpp",
   "meshcop/meshcop_tlvs.cpp",
   "meshcop/meshcop_tlvs.hpp",
+  "meshcop/network_name.cpp",
+  "meshcop/network_name.hpp",
   "meshcop/panid_query_client.cpp",
   "meshcop/panid_query_client.hpp",
   "meshcop/timestamp.cpp",
@@ -550,6 +559,7 @@
   "net/ip6_headers.hpp",
   "net/ip6_mpl.cpp",
   "net/ip6_mpl.hpp",
+  "net/ip6_types.hpp",
   "net/nd_agent.cpp",
   "net/nd_agent.hpp",
   "net/netif.cpp",
@@ -702,8 +712,8 @@
   "common/binary_search.hpp",
   "common/error.hpp",
   "common/instance.cpp",
-  "common/logging.cpp",
-  "common/random_manager.cpp",
+  "common/log.cpp",
+  "common/random.cpp",
   "common/string.cpp",
   "common/tasklet.cpp",
   "common/timer.cpp",
@@ -759,10 +769,10 @@
     "config/link_raw.h",
     "config/logging.h",
     "config/mac.h",
+    "config/misc.h",
     "config/mle.h",
     "config/netdata_publisher.h",
     "config/openthread-core-config-check.h",
-    "config/openthread-core-default-config.h",
     "config/parent_search.h",
     "config/ping_sender.h",
     "config/platform.h",
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 95ac998..e09f7e5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -101,10 +101,10 @@
     common/heap_data.cpp
     common/heap_string.cpp
     common/instance.cpp
-    common/logging.cpp
+    common/log.cpp
     common/message.cpp
     common/notifier.cpp
-    common/random_manager.cpp
+    common/random.cpp
     common/settings.cpp
     common/string.cpp
     common/tasklet.cpp
@@ -117,6 +117,7 @@
     crypto/aes_ecb.cpp
     crypto/crypto_platform.cpp
     crypto/ecdsa.cpp
+    crypto/ecdsa_tinycrypt.cpp
     crypto/hkdf_sha256.cpp
     crypto/hmac_sha256.cpp
     crypto/mbedtls.cpp
@@ -145,11 +146,13 @@
     meshcop/dataset_updater.cpp
     meshcop/dtls.cpp
     meshcop/energy_scan_client.cpp
+    meshcop/extended_panid.cpp
     meshcop/joiner.cpp
     meshcop/joiner_router.cpp
     meshcop/meshcop.cpp
     meshcop/meshcop_leader.cpp
     meshcop/meshcop_tlvs.cpp
+    meshcop/network_name.cpp
     meshcop/panid_query_client.cpp
     meshcop/timestamp.cpp
     net/checksum.cpp
@@ -246,8 +249,8 @@
     common/binary_search.cpp
     common/error.cpp
     common/instance.cpp
-    common/logging.cpp
-    common/random_manager.cpp
+    common/log.cpp
+    common/random.cpp
     common/string.cpp
     common/tasklet.cpp
     common/timer.cpp
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index c965f9e..850228d 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -191,10 +191,10 @@
     common/heap_data.cpp                          \
     common/heap_string.cpp                        \
     common/instance.cpp                           \
-    common/logging.cpp                            \
+    common/log.cpp                                \
     common/message.cpp                            \
     common/notifier.cpp                           \
-    common/random_manager.cpp                     \
+    common/random.cpp                             \
     common/settings.cpp                           \
     common/string.cpp                             \
     common/tasklet.cpp                            \
@@ -207,6 +207,7 @@
     crypto/aes_ecb.cpp                            \
     crypto/crypto_platform.cpp                    \
     crypto/ecdsa.cpp                              \
+    crypto/ecdsa_tinycrypt.cpp                    \
     crypto/hkdf_sha256.cpp                        \
     crypto/hmac_sha256.cpp                        \
     crypto/mbedtls.cpp                            \
@@ -235,11 +236,13 @@
     meshcop/dataset_updater.cpp                   \
     meshcop/dtls.cpp                              \
     meshcop/energy_scan_client.cpp                \
+    meshcop/extended_panid.cpp                    \
     meshcop/joiner.cpp                            \
     meshcop/joiner_router.cpp                     \
     meshcop/meshcop.cpp                           \
     meshcop/meshcop_leader.cpp                    \
     meshcop/meshcop_tlvs.cpp                      \
+    meshcop/network_name.cpp                      \
     meshcop/panid_query_client.cpp                \
     meshcop/timestamp.cpp                         \
     net/checksum.cpp                              \
@@ -336,8 +339,8 @@
     common/binary_search.cpp                 \
     common/error.cpp                         \
     common/instance.cpp                      \
-    common/logging.cpp                       \
-    common/random_manager.cpp                \
+    common/log.cpp                           \
+    common/random.cpp                        \
     common/string.cpp                        \
     common/tasklet.cpp                       \
     common/timer.cpp                         \
@@ -435,6 +438,7 @@
     common/extension.hpp                          \
     common/heap.hpp                               \
     common/heap_allocatable.hpp                   \
+    common/heap_array.hpp                         \
     common/heap_data.hpp                          \
     common/heap_string.hpp                        \
     common/instance.hpp                           \
@@ -442,6 +446,7 @@
     common/linked_list.hpp                        \
     common/locator.hpp                            \
     common/locator_getters.hpp                    \
+    common/log.hpp                                \
     common/logging.hpp                            \
     common/message.hpp                            \
     common/new.hpp                                \
@@ -453,7 +458,6 @@
     common/pool.hpp                               \
     common/ptr_wrapper.hpp                        \
     common/random.hpp                             \
-    common/random_manager.hpp                     \
     common/retain_ptr.hpp                         \
     common/serial_number.hpp                      \
     common/settings.hpp                           \
@@ -491,10 +495,10 @@
     config/link_raw.h                             \
     config/logging.h                              \
     config/mac.h                                  \
+    config/misc.h                                 \
     config/mle.h                                  \
     config/netdata_publisher.h                    \
     config/openthread-core-config-check.h         \
-    config/openthread-core-default-config.h       \
     config/parent_search.h                        \
     config/ping_sender.h                          \
     config/platform.h                             \
@@ -534,11 +538,13 @@
     meshcop/dataset_updater.hpp                   \
     meshcop/dtls.hpp                              \
     meshcop/energy_scan_client.hpp                \
+    meshcop/extended_panid.hpp                    \
     meshcop/joiner.hpp                            \
     meshcop/joiner_router.hpp                     \
     meshcop/meshcop.hpp                           \
     meshcop/meshcop_leader.hpp                    \
     meshcop/meshcop_tlvs.hpp                      \
+    meshcop/network_name.hpp                      \
     meshcop/panid_query_client.hpp                \
     meshcop/timestamp.hpp                         \
     net/checksum.hpp                              \
@@ -556,6 +562,7 @@
     net/ip6_filter.hpp                            \
     net/ip6_headers.hpp                           \
     net/ip6_mpl.hpp                               \
+    net/ip6_types.hpp                             \
     net/nd_agent.hpp                              \
     net/netif.hpp                                 \
     net/sntp_client.hpp                           \
diff --git a/src/core/api/backbone_router_ftd_api.cpp b/src/core/api/backbone_router_ftd_api.cpp
index b7e38e3..d8e5169 100644
--- a/src/core/api/backbone_router_ftd_api.cpp
+++ b/src/core/api/backbone_router_ftd_api.cpp
@@ -177,11 +177,6 @@
 }
 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
 
-void otBackboneRouterConfigSkipSeqNumIncrease(otInstance *aInstance, bool aSkip)
-{
-    AsCoreType(aInstance).Get<BackboneRouter::Local>().ConfigSkipSeqNumIncrease(aSkip);
-}
-
 #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
 
 #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
diff --git a/src/core/api/commissioner_api.cpp b/src/core/api/commissioner_api.cpp
index 4f5aa4b..9c5b575 100644
--- a/src/core/api/commissioner_api.cpp
+++ b/src/core/api/commissioner_api.cpp
@@ -50,6 +50,16 @@
     return AsCoreType(aInstance).Get<MeshCoP::Commissioner>().Start(aStateCallback, aJoinerCallback, aCallbackContext);
 }
 
+const char *otCommissionerGetId(otInstance *aInstance)
+{
+    return AsCoreType(aInstance).Get<MeshCoP::Commissioner>().GetId();
+}
+
+otError otCommissionerSetId(otInstance *aInstance, const char *aId)
+{
+    return AsCoreType(aInstance).Get<MeshCoP::Commissioner>().SetId(aId);
+}
+
 otError otCommissionerStop(otInstance *aInstance)
 {
     return AsCoreType(aInstance).Get<MeshCoP::Commissioner>().Stop();
diff --git a/src/core/api/dataset_api.cpp b/src/core/api/dataset_api.cpp
index a4153d2..457c783 100644
--- a/src/core/api/dataset_api.cpp
+++ b/src/core/api/dataset_api.cpp
@@ -44,63 +44,63 @@
 
 bool otDatasetIsCommissioned(otInstance *aInstance)
 {
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().IsCommissioned();
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().IsCommissioned();
 }
 
 otError otDatasetGetActive(otInstance *aInstance, otOperationalDataset *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().Read(AsCoreType(aDataset));
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().Read(AsCoreType(aDataset));
 }
 
 otError otDatasetGetActiveTlvs(otInstance *aInstance, otOperationalDatasetTlvs *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().Read(*aDataset);
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().Read(*aDataset);
 }
 
 otError otDatasetSetActive(otInstance *aInstance, const otOperationalDataset *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().Save(AsCoreType(aDataset));
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().Save(AsCoreType(aDataset));
 }
 
 otError otDatasetSetActiveTlvs(otInstance *aInstance, const otOperationalDatasetTlvs *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().Save(*aDataset);
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().Save(*aDataset);
 }
 
 otError otDatasetGetPending(otInstance *aInstance, otOperationalDataset *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().Read(AsCoreType(aDataset));
+    return AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().Read(AsCoreType(aDataset));
 }
 
 otError otDatasetGetPendingTlvs(otInstance *aInstance, otOperationalDatasetTlvs *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().Read(*aDataset);
+    return AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().Read(*aDataset);
 }
 
 otError otDatasetSetPending(otInstance *aInstance, const otOperationalDataset *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().Save(AsCoreType(aDataset));
+    return AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().Save(AsCoreType(aDataset));
 }
 
 otError otDatasetSetPendingTlvs(otInstance *aInstance, const otOperationalDatasetTlvs *aDataset)
 {
     OT_ASSERT(aDataset != nullptr);
 
-    return AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().Save(*aDataset);
+    return AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().Save(*aDataset);
 }
 
 otError otDatasetSendMgmtActiveGet(otInstance *                          aInstance,
@@ -109,8 +109,8 @@
                                    uint8_t                               aLength,
                                    const otIp6Address *                  aAddress)
 {
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().SendGetRequest(AsCoreType(aDatasetComponents), aTlvTypes,
-                                                                              aLength, aAddress);
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().SendGetRequest(AsCoreType(aDatasetComponents),
+                                                                                     aTlvTypes, aLength, aAddress);
 }
 
 otError otDatasetSendMgmtActiveSet(otInstance *                aInstance,
@@ -120,8 +120,8 @@
                                    otDatasetMgmtSetCallback    aCallback,
                                    void *                      aContext)
 {
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().SendSetRequest(AsCoreType(aDataset), aTlvs, aLength,
-                                                                              aCallback, aContext);
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().SendSetRequest(AsCoreType(aDataset), aTlvs,
+                                                                                     aLength, aCallback, aContext);
 }
 
 otError otDatasetSendMgmtPendingGet(otInstance *                          aInstance,
@@ -130,8 +130,8 @@
                                     uint8_t                               aLength,
                                     const otIp6Address *                  aAddress)
 {
-    return AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().SendGetRequest(AsCoreType(aDatasetComponents),
-                                                                               aTlvTypes, aLength, aAddress);
+    return AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().SendGetRequest(AsCoreType(aDatasetComponents),
+                                                                                      aTlvTypes, aLength, aAddress);
 }
 
 otError otDatasetSendMgmtPendingSet(otInstance *                aInstance,
@@ -141,8 +141,8 @@
                                     otDatasetMgmtSetCallback    aCallback,
                                     void *                      aContext)
 {
-    return AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().SendSetRequest(AsCoreType(aDataset), aTlvs, aLength,
-                                                                               aCallback, aContext);
+    return AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().SendSetRequest(AsCoreType(aDataset), aTlvs,
+                                                                                      aLength, aCallback, aContext);
 }
 
 #if OPENTHREAD_FTD
diff --git a/src/core/api/dataset_ftd_api.cpp b/src/core/api/dataset_ftd_api.cpp
index 4c26088..ea1792d 100644
--- a/src/core/api/dataset_ftd_api.cpp
+++ b/src/core/api/dataset_ftd_api.cpp
@@ -44,7 +44,7 @@
 
 otError otDatasetCreateNewNetwork(otInstance *aInstance, otOperationalDataset *aDataset)
 {
-    return AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().CreateNewNetwork(AsCoreType(aDataset));
+    return AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().CreateNewNetwork(AsCoreType(aDataset));
 }
 
 uint32_t otDatasetGetDelayTimerMinimal(otInstance *aInstance)
diff --git a/src/core/api/dns_api.cpp b/src/core/api/dns_api.cpp
index ed3bfcf..d9bdda7 100644
--- a/src/core/api/dns_api.cpp
+++ b/src/core/api/dns_api.cpp
@@ -91,6 +91,18 @@
                                                                    AsCoreTypePtr(aConfig));
 }
 
+#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE
+otError otDnsClientResolveIp4Address(otInstance *            aInstance,
+                                     const char *            aHostName,
+                                     otDnsAddressCallback    aCallback,
+                                     void *                  aContext,
+                                     const otDnsQueryConfig *aConfig)
+{
+    return AsCoreType(aInstance).Get<Dns::Client>().ResolveIp4Address(aHostName, aCallback, aContext,
+                                                                      AsCoreTypePtr(aConfig));
+}
+#endif
+
 otError otDnsAddressResponseGetHostName(const otDnsAddressResponse *aResponse,
                                         char *                      aNameBuffer,
                                         uint16_t                    aNameBufferSize)
diff --git a/src/core/api/instance_api.cpp b/src/core/api/instance_api.cpp
index f9cc00e..5557f68 100644
--- a/src/core/api/instance_api.cpp
+++ b/src/core/api/instance_api.cpp
@@ -38,7 +38,6 @@
 
 #include "common/as_core_type.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 #include "common/new.hpp"
 #include "radio/radio.hpp"
 
@@ -51,7 +50,7 @@
 #endif
 #else // __ANDROID__
 #if defined(__DATE__)
-#define OPENTHREAD_BUILD_DATETIME "; " __DATE__ " " __TIME__
+#define OPENTHREAD_BUILD_DATETIME __DATE__ " " __TIME__
 #endif
 #endif // __ANDROID__
 #endif // !defined(OPENTHREAD_BUILD_DATETIME)
@@ -64,7 +63,6 @@
     Instance *instance;
 
     instance = Instance::Init(aInstanceBuffer, aInstanceBufferSize);
-    otLogInfoApi("otInstance Initialized");
 
     return instance;
 }
@@ -179,10 +177,10 @@
 #endif
     const char sVersion[] = PACKAGE_NAME "/" PACKAGE_VERSION "; " OPENTHREAD_CONFIG_PLATFORM_INFO
 #ifdef OPENTHREAD_BUILD_DATETIME
-        OPENTHREAD_BUILD_DATETIME
+                                         "; " OPENTHREAD_BUILD_DATETIME
 #endif
 #ifdef PLATFORM_VERSION_ATTR_SUFFIX
-            PLATFORM_VERSION_ATTR_SUFFIX
+                                             PLATFORM_VERSION_ATTR_SUFFIX
 #endif
         ; // Trailing semicolon to end statement.
 
diff --git a/src/core/api/ip6_api.cpp b/src/core/api/ip6_api.cpp
index a65dc55..b12b35e 100644
--- a/src/core/api/ip6_api.cpp
+++ b/src/core/api/ip6_api.cpp
@@ -37,7 +37,6 @@
 
 #include "common/as_core_type.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 #include "utils/slaac_address.hpp"
 
 using namespace ot;
diff --git a/src/core/api/link_api.cpp b/src/core/api/link_api.cpp
index a1d38be..ff5922d 100644
--- a/src/core/api/link_api.cpp
+++ b/src/core/api/link_api.cpp
@@ -77,8 +77,8 @@
     VerifyOrExit(instance.Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
     SuccessOrExit(error = instance.Get<Mac::Mac>().SetPanChannel(aChannel));
-    instance.Get<MeshCoP::ActiveDataset>().Clear();
-    instance.Get<MeshCoP::PendingDataset>().Clear();
+    instance.Get<MeshCoP::ActiveDatasetManager>().Clear();
+    instance.Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -141,8 +141,8 @@
     VerifyOrExit(instance.Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
     instance.Get<Mac::Mac>().SetPanId(aPanId);
-    instance.Get<MeshCoP::ActiveDataset>().Clear();
-    instance.Get<MeshCoP::PendingDataset>().Clear();
+    instance.Get<MeshCoP::ActiveDatasetManager>().Clear();
+    instance.Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -288,7 +288,7 @@
 int8_t otLinkConvertLinkQualityToRss(otInstance *aInstance, uint8_t aLinkQuality)
 {
     return LinkQualityInfo::ConvertLinkQualityToRss(AsCoreType(aInstance).Get<Mac::Mac>().GetNoiseFloor(),
-                                                    aLinkQuality);
+                                                    static_cast<LinkQuality>(aLinkQuality));
 }
 
 #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
diff --git a/src/core/api/logging_api.cpp b/src/core/api/logging_api.cpp
index b00147c..3218837 100644
--- a/src/core/api/logging_api.cpp
+++ b/src/core/api/logging_api.cpp
@@ -33,19 +33,17 @@
 
 #include "openthread-core-config.h"
 
-#include <openthread/logging.h>
+#include "common/code_utils.hpp"
+#include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
+#include "common/log.hpp"
 
 using namespace ot;
 
 otLogLevel otLoggingGetLevel(void)
 {
-#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
-    return Instance::GetLogLevel();
-#else
-    return static_cast<otLogLevel>(OPENTHREAD_CONFIG_LOG_LEVEL);
-#endif
+    return static_cast<otLogLevel>(Instance::GetLogLevel());
 }
 
 #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
@@ -53,15 +51,154 @@
 {
     Error error = kErrorNone;
 
-    if (aLogLevel <= OT_LOG_LEVEL_DEBG && aLogLevel >= OT_LOG_LEVEL_NONE)
-    {
-        Instance::SetLogLevel(aLogLevel);
-    }
-    else
-    {
-        error = kErrorInvalidArgs;
-    }
+    VerifyOrExit(aLogLevel <= kLogLevelDebg && aLogLevel >= kLogLevelNone, error = kErrorInvalidArgs);
+    Instance::SetLogLevel(static_cast<LogLevel>(aLogLevel));
 
+exit:
     return error;
 }
 #endif
+
+static const char kPlatformModuleName[] = "Platform";
+
+void otLogCritPlat(const char *aFormat, ...)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_CRIT) && OPENTHREAD_CONFIG_LOG_PLATFORM
+    va_list args;
+
+    va_start(args, aFormat);
+    Logger::LogVarArgs(kPlatformModuleName, kLogLevelCrit, aFormat, args);
+    va_end(args);
+#else
+    OT_UNUSED_VARIABLE(aFormat);
+    OT_UNUSED_VARIABLE(kPlatformModuleName);
+#endif
+}
+
+void otLogWarnPlat(const char *aFormat, ...)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) && OPENTHREAD_CONFIG_LOG_PLATFORM
+    va_list args;
+
+    va_start(args, aFormat);
+    Logger::LogVarArgs(kPlatformModuleName, kLogLevelWarn, aFormat, args);
+    va_end(args);
+#else
+    OT_UNUSED_VARIABLE(aFormat);
+#endif
+}
+
+void otLogNotePlat(const char *aFormat, ...)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE) && OPENTHREAD_CONFIG_LOG_PLATFORM
+    va_list args;
+
+    va_start(args, aFormat);
+    Logger::LogVarArgs(kPlatformModuleName, kLogLevelNote, aFormat, args);
+    va_end(args);
+#else
+    OT_UNUSED_VARIABLE(aFormat);
+#endif
+}
+
+void otLogInfoPlat(const char *aFormat, ...)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) && OPENTHREAD_CONFIG_LOG_PLATFORM
+    va_list args;
+
+    va_start(args, aFormat);
+    Logger::LogVarArgs(kPlatformModuleName, kLogLevelInfo, aFormat, args);
+    va_end(args);
+#else
+    OT_UNUSED_VARIABLE(aFormat);
+#endif
+}
+
+void otLogDebgPlat(const char *aFormat, ...)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG) && OPENTHREAD_CONFIG_LOG_PLATFORM
+    va_list args;
+
+    va_start(args, aFormat);
+    Logger::LogVarArgs(kPlatformModuleName, kLogLevelDebg, aFormat, args);
+    va_end(args);
+#else
+    OT_UNUSED_VARIABLE(aFormat);
+#endif
+}
+
+void otDumpCritPlat(const char *aText, const void *aData, uint16_t aDataLength)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_CRIT) && OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+    Logger::DumpInModule(kPlatformModuleName, kLogLevelCrit, aText, aData, aDataLength);
+#else
+    OT_UNUSED_VARIABLE(aText);
+    OT_UNUSED_VARIABLE(aData);
+    OT_UNUSED_VARIABLE(aDataLength);
+#endif
+}
+
+void otDumpWarnPlat(const char *aText, const void *aData, uint16_t aDataLength)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) && OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+    Logger::DumpInModule(kPlatformModuleName, kLogLevelWarn, aText, aData, aDataLength);
+#else
+    OT_UNUSED_VARIABLE(aText);
+    OT_UNUSED_VARIABLE(aData);
+    OT_UNUSED_VARIABLE(aDataLength);
+#endif
+}
+
+void otDumpNotePlat(const char *aText, const void *aData, uint16_t aDataLength)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE) && OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+    Logger::DumpInModule(kPlatformModuleName, kLogLevelNote, aText, aData, aDataLength);
+#else
+    OT_UNUSED_VARIABLE(aText);
+    OT_UNUSED_VARIABLE(aData);
+    OT_UNUSED_VARIABLE(aDataLength);
+#endif
+}
+
+void otDumpInfoPlat(const char *aText, const void *aData, uint16_t aDataLength)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) && OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+    Logger::DumpInModule(kPlatformModuleName, kLogLevelInfo, aText, aData, aDataLength);
+#else
+    OT_UNUSED_VARIABLE(aText);
+    OT_UNUSED_VARIABLE(aData);
+    OT_UNUSED_VARIABLE(aDataLength);
+#endif
+}
+
+void otDumpDebgPlat(const char *aText, const void *aData, uint16_t aDataLength)
+{
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG) && OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+    Logger::DumpInModule(kPlatformModuleName, kLogLevelDebg, aText, aData, aDataLength);
+#else
+    OT_UNUSED_VARIABLE(aText);
+    OT_UNUSED_VARIABLE(aData);
+    OT_UNUSED_VARIABLE(aDataLength);
+#endif
+}
+
+void otLogCli(otLogLevel aLogLevel, const char *aFormat, ...)
+{
+#if OT_SHOULD_LOG && OPENTHREAD_CONFIG_LOG_CLI
+    static const char kCliModuleName[] = "Cli";
+
+    va_list args;
+
+    OT_ASSERT(aLogLevel >= kLogLevelNone && aLogLevel <= kLogLevelDebg);
+    VerifyOrExit(aLogLevel >= kLogLevelNone && aLogLevel <= kLogLevelDebg);
+
+    va_start(args, aFormat);
+    Logger::LogVarArgs(kCliModuleName, static_cast<LogLevel>(aLogLevel), aFormat, args);
+    va_end(args);
+exit:
+#else
+    OT_UNUSED_VARIABLE(aLogLevel);
+    OT_UNUSED_VARIABLE(aFormat);
+#endif
+    return;
+}
diff --git a/src/core/api/message_api.cpp b/src/core/api/message_api.cpp
index 81d9baf..ecc3bfc 100644
--- a/src/core/api/message_api.cpp
+++ b/src/core/api/message_api.cpp
@@ -145,61 +145,6 @@
 #if OPENTHREAD_MTD || OPENTHREAD_FTD
 void otMessageGetBufferInfo(otInstance *aInstance, otBufferInfo *aBufferInfo)
 {
-    uint16_t  messages, buffers;
-    Instance &instance = AsCoreType(aInstance);
-
-    aBufferInfo->mTotalBuffers = instance.Get<MessagePool>().GetTotalBufferCount();
-
-    aBufferInfo->mFreeBuffers = instance.Get<MessagePool>().GetFreeBufferCount();
-
-    instance.Get<MeshForwarder>().GetSendQueue().GetInfo(aBufferInfo->m6loSendMessages, aBufferInfo->m6loSendBuffers);
-
-    instance.Get<MeshForwarder>().GetReassemblyQueue().GetInfo(aBufferInfo->m6loReassemblyMessages,
-                                                               aBufferInfo->m6loReassemblyBuffers);
-
-#if OPENTHREAD_FTD
-    instance.Get<MeshForwarder>().GetResolvingQueue().GetInfo(aBufferInfo->mArpMessages, aBufferInfo->mArpBuffers);
-#else
-    aBufferInfo->mArpMessages             = 0;
-    aBufferInfo->mArpBuffers              = 0;
-#endif
-
-    instance.Get<Ip6::Ip6>().GetSendQueue().GetInfo(aBufferInfo->mIp6Messages, aBufferInfo->mIp6Buffers);
-
-#if OPENTHREAD_FTD
-    instance.Get<Ip6::Mpl>().GetBufferedMessageSet().GetInfo(aBufferInfo->mMplMessages, aBufferInfo->mMplBuffers);
-#else
-    aBufferInfo->mMplMessages             = 0;
-    aBufferInfo->mMplBuffers              = 0;
-#endif
-
-    instance.Get<Mle::MleRouter>().GetMessageQueue().GetInfo(aBufferInfo->mMleMessages, aBufferInfo->mMleBuffers);
-
-    instance.Get<Tmf::Agent>().GetRequestMessages().GetInfo(aBufferInfo->mCoapMessages, aBufferInfo->mCoapBuffers);
-    instance.Get<Tmf::Agent>().GetCachedResponses().GetInfo(messages, buffers);
-    aBufferInfo->mCoapMessages += messages;
-    aBufferInfo->mCoapBuffers += buffers;
-
-#if OPENTHREAD_CONFIG_DTLS_ENABLE
-    instance.Get<Coap::CoapSecure>().GetRequestMessages().GetInfo(aBufferInfo->mCoapSecureMessages,
-                                                                  aBufferInfo->mCoapSecureBuffers);
-    instance.Get<Coap::CoapSecure>().GetCachedResponses().GetInfo(messages, buffers);
-    aBufferInfo->mCoapSecureMessages += messages;
-    aBufferInfo->mCoapSecureBuffers += buffers;
-#else
-    aBufferInfo->mCoapSecureMessages      = 0;
-    aBufferInfo->mCoapSecureBuffers       = 0;
-#endif
-
-#if OPENTHREAD_CONFIG_COAP_API_ENABLE
-    instance.GetApplicationCoap().GetRequestMessages().GetInfo(aBufferInfo->mApplicationCoapMessages,
-                                                               aBufferInfo->mApplicationCoapBuffers);
-    instance.GetApplicationCoap().GetCachedResponses().GetInfo(messages, buffers);
-    aBufferInfo->mApplicationCoapMessages += messages;
-    aBufferInfo->mApplicationCoapBuffers += buffers;
-#else
-    aBufferInfo->mApplicationCoapMessages = 0;
-    aBufferInfo->mApplicationCoapBuffers  = 0;
-#endif
+    AsCoreType(aInstance).GetBufferInfo(AsCoreType(aBufferInfo));
 }
 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
diff --git a/src/core/api/netdata_api.cpp b/src/core/api/netdata_api.cpp
index e646645..c28b987 100644
--- a/src/core/api/netdata_api.cpp
+++ b/src/core/api/netdata_api.cpp
@@ -60,6 +60,13 @@
     return error;
 }
 
+#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+bool otNetDataContainsOmrPrefix(otInstance *aInstance, const otIp6Prefix *aPrefix)
+{
+    return AsCoreType(aInstance).Get<NetworkData::Leader>().ContainsOmrPrefix(AsCoreType(aPrefix));
+}
+#endif
+
 otError otNetDataGetNextRoute(otInstance *aInstance, otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig)
 {
     Error error = kErrorNone;
diff --git a/src/core/api/tasklet_api.cpp b/src/core/api/tasklet_api.cpp
index 6f8a0de..e5213c8 100644
--- a/src/core/api/tasklet_api.cpp
+++ b/src/core/api/tasklet_api.cpp
@@ -38,7 +38,6 @@
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 
 using namespace ot;
 
diff --git a/src/core/api/tcp_api.cpp b/src/core/api/tcp_api.cpp
index 8721d1e..896aac6 100644
--- a/src/core/api/tcp_api.cpp
+++ b/src/core/api/tcp_api.cpp
@@ -42,7 +42,9 @@
 
 using namespace ot;
 
-otError otTcpEndpointInitialize(otInstance *aInstance, otTcpEndpoint *aEndpoint, otTcpEndpointInitializeArgs *aArgs)
+otError otTcpEndpointInitialize(otInstance *                       aInstance,
+                                otTcpEndpoint *                    aEndpoint,
+                                const otTcpEndpointInitializeArgs *aArgs)
 {
     return AsCoreType(aEndpoint).Initialize(AsCoreType(aInstance), *aArgs);
 }
@@ -117,7 +119,9 @@
     return AsCoreType(aEndpoint).Deinitialize();
 }
 
-otError otTcpListenerInitialize(otInstance *aInstance, otTcpListener *aListener, otTcpListenerInitializeArgs *aArgs)
+otError otTcpListenerInitialize(otInstance *                       aInstance,
+                                otTcpListener *                    aListener,
+                                const otTcpListenerInitializeArgs *aArgs)
 {
     return AsCoreType(aListener).Initialize(AsCoreType(aInstance), *aArgs);
 }
diff --git a/src/core/api/thread_api.cpp b/src/core/api/thread_api.cpp
index 9ebafa9..b6ab068 100644
--- a/src/core/api/thread_api.cpp
+++ b/src/core/api/thread_api.cpp
@@ -55,25 +55,25 @@
 
 const otExtendedPanId *otThreadGetExtendedPanId(otInstance *aInstance)
 {
-    return &AsCoreType(aInstance).Get<Mac::Mac>().GetExtendedPanId();
+    return &AsCoreType(aInstance).Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId();
 }
 
 otError otThreadSetExtendedPanId(otInstance *aInstance, const otExtendedPanId *aExtendedPanId)
 {
-    Error                     error    = kErrorNone;
-    Instance &                instance = AsCoreType(aInstance);
-    const Mac::ExtendedPanId &extPanId = AsCoreType(aExtendedPanId);
-    Mle::MeshLocalPrefix      prefix;
+    Error                         error    = kErrorNone;
+    Instance &                    instance = AsCoreType(aInstance);
+    const MeshCoP::ExtendedPanId &extPanId = AsCoreType(aExtendedPanId);
+    Mle::MeshLocalPrefix          prefix;
 
     VerifyOrExit(instance.Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
-    instance.Get<Mac::Mac>().SetExtendedPanId(extPanId);
+    instance.Get<MeshCoP::ExtendedPanIdManager>().SetExtPanId(extPanId);
 
     prefix.SetFromExtendedPanId(extPanId);
     instance.Get<Mle::MleRouter>().SetMeshLocalPrefix(prefix);
 
-    instance.Get<MeshCoP::ActiveDataset>().Clear();
-    instance.Get<MeshCoP::PendingDataset>().Clear();
+    instance.Get<MeshCoP::ActiveDatasetManager>().Clear();
+    instance.Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -123,8 +123,8 @@
 
     instance.Get<KeyManager>().SetNetworkKey(AsCoreType(aKey));
 
-    instance.Get<MeshCoP::ActiveDataset>().Clear();
-    instance.Get<MeshCoP::PendingDataset>().Clear();
+    instance.Get<MeshCoP::ActiveDatasetManager>().Clear();
+    instance.Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -141,8 +141,8 @@
     VerifyOrExit(instance.Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
     instance.Get<KeyManager>().SetNetworkKeyRef((aKeyRef));
-    instance.Get<MeshCoP::ActiveDataset>().Clear();
-    instance.Get<MeshCoP::PendingDataset>().Clear();
+    instance.Get<MeshCoP::ActiveDatasetManager>().Clear();
+    instance.Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -171,8 +171,8 @@
     VerifyOrExit(AsCoreType(aInstance).Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
     AsCoreType(aInstance).Get<Mle::MleRouter>().SetMeshLocalPrefix(AsCoreType(aMeshLocalPrefix));
-    AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().Clear();
-    AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().Clear();
+    AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().Clear();
+    AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -200,7 +200,7 @@
 
 const char *otThreadGetNetworkName(otInstance *aInstance)
 {
-    return AsCoreType(aInstance).Get<Mac::Mac>().GetNetworkName().GetAsCString();
+    return AsCoreType(aInstance).Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsCString();
 }
 
 otError otThreadSetNetworkName(otInstance *aInstance, const char *aNetworkName)
@@ -209,9 +209,9 @@
 
     VerifyOrExit(AsCoreType(aInstance).Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
-    error = AsCoreType(aInstance).Get<Mac::Mac>().SetNetworkName(aNetworkName);
-    AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().Clear();
-    AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().Clear();
+    error = AsCoreType(aInstance).Get<MeshCoP::NetworkNameManager>().SetNetworkName(aNetworkName);
+    AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().Clear();
+    AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -220,7 +220,7 @@
 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
 const char *otThreadGetDomainName(otInstance *aInstance)
 {
-    return AsCoreType(aInstance).Get<Mac::Mac>().GetDomainName().GetAsCString();
+    return AsCoreType(aInstance).Get<MeshCoP::NetworkNameManager>().GetDomainName().GetAsCString();
 }
 
 otError otThreadSetDomainName(otInstance *aInstance, const char *aDomainName)
@@ -229,7 +229,7 @@
 
     VerifyOrExit(AsCoreType(aInstance).Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
-    error = AsCoreType(aInstance).Get<Mac::Mac>().SetDomainName(aDomainName);
+    error = AsCoreType(aInstance).Get<MeshCoP::NetworkNameManager>().SetDomainName(aDomainName);
 
 exit:
     return error;
@@ -295,7 +295,7 @@
 
 otError otThreadBecomeChild(otInstance *aInstance)
 {
-    return AsCoreType(aInstance).Get<Mle::MleRouter>().BecomeChild(Mle::kAttachAny);
+    return AsCoreType(aInstance).Get<Mle::MleRouter>().BecomeChild();
 }
 
 otError otThreadGetNextNeighborInfo(otInstance *aInstance, otNeighborInfoIterator *aIterator, otNeighborInfo *aInfo)
diff --git a/src/core/api/thread_ftd_api.cpp b/src/core/api/thread_ftd_api.cpp
index 3333e85..e8d3a4b 100644
--- a/src/core/api/thread_ftd_api.cpp
+++ b/src/core/api/thread_ftd_api.cpp
@@ -296,8 +296,8 @@
     VerifyOrExit(AsCoreType(aInstance).Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
     AsCoreType(aInstance).Get<KeyManager>().SetPskc(AsCoreType(aPskc));
-    AsCoreType(aInstance).Get<MeshCoP::ActiveDataset>().Clear();
-    AsCoreType(aInstance).Get<MeshCoP::PendingDataset>().Clear();
+    AsCoreType(aInstance).Get<MeshCoP::ActiveDatasetManager>().Clear();
+    AsCoreType(aInstance).Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
@@ -313,8 +313,8 @@
     VerifyOrExit(instance.Get<Mle::MleRouter>().IsDisabled(), error = kErrorInvalidState);
 
     instance.Get<KeyManager>().SetPskcRef(aKeyRef);
-    instance.Get<MeshCoP::ActiveDataset>().Clear();
-    instance.Get<MeshCoP::PendingDataset>().Clear();
+    instance.Get<MeshCoP::ActiveDatasetManager>().Clear();
+    instance.Get<MeshCoP::PendingDatasetManager>().Clear();
 
 exit:
     return error;
diff --git a/src/core/backbone_router/backbone_tmf.cpp b/src/core/backbone_router/backbone_tmf.cpp
index 676906f..293793f 100644
--- a/src/core/backbone_router/backbone_tmf.cpp
+++ b/src/core/backbone_router/backbone_tmf.cpp
@@ -36,11 +36,13 @@
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
 
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 
 namespace ot {
 namespace BackboneRouter {
 
+RegisterLogModule("Bbr");
+
 Error BackboneTmfAgent::Start(void)
 {
     Error error = kErrorNone;
@@ -96,11 +98,11 @@
 
     if (aError == kErrorNone)
     {
-        otLogInfoBbr("%s %s: %s", aText, aAddress.ToString().AsCString(), ErrorToString(aError));
+        LogInfo("%s %s: %s", aText, aAddress.ToString().AsCString(), ErrorToString(aError));
     }
     else
     {
-        otLogWarnBbr("%s %s: %s", aText, aAddress.ToString().AsCString(), ErrorToString(aError));
+        LogWarn("%s %s: %s", aText, aAddress.ToString().AsCString(), ErrorToString(aError));
     }
 }
 
diff --git a/src/core/backbone_router/bbr_leader.cpp b/src/core/backbone_router/bbr_leader.cpp
index 41df0c1..4b64fff 100644
--- a/src/core/backbone_router/bbr_leader.cpp
+++ b/src/core/backbone_router/bbr_leader.cpp
@@ -37,11 +37,12 @@
 
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
 
 namespace ot {
 namespace BackboneRouter {
 
+RegisterLogModule("BbrLeader");
+
 Leader::Leader(Instance &aInstance)
     : InstanceLocator(aInstance)
 {
@@ -81,24 +82,24 @@
     return error;
 }
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_BBR == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 void Leader::LogBackboneRouterPrimary(State aState, const BackboneRouterConfig &aConfig) const
 {
     OT_UNUSED_VARIABLE(aConfig);
 
-    otLogInfoBbr("PBBR state: %s", StateToString(aState));
+    LogInfo("PBBR state: %s", StateToString(aState));
 
     if (aState != kStateRemoved && aState != kStateNone)
     {
-        otLogInfoBbr("Rloc16: 0x%4X, seqno: %d, delay: %d, timeout %d", aConfig.mServer16, aConfig.mSequenceNumber,
-                     aConfig.mReregistrationDelay, aConfig.mMlrTimeout);
+        LogInfo("Rloc16: 0x%4X, seqno: %d, delay: %d, timeout %d", aConfig.mServer16, aConfig.mSequenceNumber,
+                aConfig.mReregistrationDelay, aConfig.mMlrTimeout);
     }
 }
 
 void Leader::LogDomainPrefix(DomainPrefixState aState, const Ip6::Prefix &aPrefix) const
 {
-    otLogInfoBbr("Domain Prefix: %s, state: %s", aPrefix.ToString().AsCString(), DomainPrefixStateToString(aState));
+    LogInfo("Domain Prefix: %s, state: %s", aPrefix.ToString().AsCString(), DomainPrefixStateToString(aState));
 }
 
 const char *Leader::StateToString(State aState)
@@ -141,7 +142,7 @@
     return kPrefixStateStrings[aState];
 }
 
-#endif // (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_BBR == 1)
+#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 
 void Leader::Update(void)
 {
@@ -204,7 +205,7 @@
 
         if (config.mMlrTimeout != origMlrTimeout)
         {
-            otLogNoteBbr("Leader MLR Timeout is normalized from %u to %u", origMlrTimeout, config.mMlrTimeout);
+            LogNote("Leader MLR Timeout is normalized from %u to %u", origMlrTimeout, config.mMlrTimeout);
         }
     }
 
diff --git a/src/core/backbone_router/bbr_leader.hpp b/src/core/backbone_router/bbr_leader.hpp
index 823d619..6266d02 100644
--- a/src/core/backbone_router/bbr_leader.hpp
+++ b/src/core/backbone_router/bbr_leader.hpp
@@ -44,6 +44,7 @@
 #include "coap/coap.hpp"
 #include "coap/coap_message.hpp"
 #include "common/locator.hpp"
+#include "common/log.hpp"
 #include "common/non_copyable.hpp"
 #include "net/ip6_address.hpp"
 
@@ -175,7 +176,7 @@
 private:
     void UpdateBackboneRouterPrimary(void);
     void UpdateDomainPrefixConfig(void);
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_BBR == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
     void               LogBackboneRouterPrimary(State aState, const BackboneRouterConfig &aConfig) const;
     void               LogDomainPrefix(DomainPrefixState aState, const Ip6::Prefix &aPrefix) const;
     static const char *StateToString(State aState);
diff --git a/src/core/backbone_router/bbr_local.cpp b/src/core/backbone_router/bbr_local.cpp
index 862860f..4c7945a 100644
--- a/src/core/backbone_router/bbr_local.cpp
+++ b/src/core/backbone_router/bbr_local.cpp
@@ -38,7 +38,7 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "thread/mle_types.hpp"
 #include "thread/thread_netif.hpp"
@@ -47,6 +47,8 @@
 
 namespace BackboneRouter {
 
+RegisterLogModule("BbrLocal");
+
 Local::Local(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mState(OT_BACKBONE_ROUTER_STATE_DISABLED)
@@ -57,9 +59,6 @@
     , mIsServiceAdded(false)
     , mDomainPrefixCallback(nullptr)
     , mDomainPrefixCallbackContext(nullptr)
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-    , mSkipSeqNumIncrease(false)
-#endif
 {
     mDomainPrefixConfig.GetPrefix().SetLength(0);
 
@@ -273,19 +272,7 @@
         mSequenceNumber      = aConfig.mSequenceNumber;
         mReregistrationDelay = aConfig.mReregistrationDelay;
         mMlrTimeout          = aConfig.mMlrTimeout;
-
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-        if (mSkipSeqNumIncrease)
-        {
-            // BBR-TC-02 forces Sequence Number for the reference device with raw UDP API
-            // Do not increase Sequence Number in that case.
-        }
-        else
-#endif
-        {
-            SequenceNumberIncrease();
-        }
-
+        SequenceNumberIncrease();
         Get<Notifier>().Signal(kEventThreadBackboneRouterLocalChanged);
         if (AddService(true /* Force registration to refresh and restore Primary state */) == kErrorNone)
         {
@@ -458,17 +445,17 @@
     LogDomainPrefix("Add", error);
 }
 
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_BBR == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
 void Local::LogDomainPrefix(const char *aAction, Error aError)
 {
-    otLogInfoBbr("%s Domain Prefix: %s, %s", aAction, mDomainPrefixConfig.GetPrefix().ToString().AsCString(),
-                 ErrorToString(aError));
+    LogInfo("%s Domain Prefix: %s, %s", aAction, mDomainPrefixConfig.GetPrefix().ToString().AsCString(),
+            ErrorToString(aError));
 }
 
 void Local::LogBackboneRouterService(const char *aAction, Error aError)
 {
-    otLogInfoBbr("%s BBR Service: seqno (%d), delay (%ds), timeout (%ds), %s", aAction, mSequenceNumber,
-                 mReregistrationDelay, mMlrTimeout, ErrorToString(aError));
+    LogInfo("%s BBR Service: seqno (%d), delay (%ds), timeout (%ds), %s", aAction, mSequenceNumber,
+            mReregistrationDelay, mMlrTimeout, ErrorToString(aError));
 }
 #endif
 
@@ -478,13 +465,6 @@
     mDomainPrefixCallbackContext = aContext;
 }
 
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-void Local::ConfigSkipSeqNumIncrease(bool aSkip)
-{
-    mSkipSeqNumIncrease = aSkip;
-}
-#endif
-
 } // namespace BackboneRouter
 
 } // namespace ot
diff --git a/src/core/backbone_router/bbr_local.hpp b/src/core/backbone_router/bbr_local.hpp
index 23c337a..b7a76f3 100644
--- a/src/core/backbone_router/bbr_local.hpp
+++ b/src/core/backbone_router/bbr_local.hpp
@@ -55,6 +55,7 @@
 
 #include "backbone_router/bbr_leader.hpp"
 #include "common/locator.hpp"
+#include "common/log.hpp"
 #include "common/non_copyable.hpp"
 #include "net/netif.hpp"
 #include "thread/network_data.hpp"
@@ -254,27 +255,13 @@
      */
     void SetDomainPrefixCallback(otBackboneRouterDomainPrefixCallback aCallback, void *aContext);
 
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-    /**
-     * This method configures the ability to increase or not the BBR Dataset Sequence Number when a
-     * BBR recovers its BBR Dataset from the Leader's Network Data.
-     *
-     * Note: available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled.
-     *       Only used for certification.
-     *
-     * @param[in] aSkip  Whether to skip the increase of Sequence Number or not.
-     *
-     */
-    void ConfigSkipSeqNumIncrease(bool aSkip);
-#endif
-
 private:
     void SetState(BackboneRouterState aState);
     void RemoveService(void);
     void AddDomainPrefixToNetworkData(void);
     void RemoveDomainPrefixFromNetworkData(void);
     void SequenceNumberIncrease(void);
-#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_BBR == 1)
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
     void LogBackboneRouterService(const char *aAction, Error aError);
     void LogDomainPrefix(const char *aAction, Error aError);
 #else
@@ -300,10 +287,6 @@
     Ip6::Address                         mAllDomainBackboneRouters;
     otBackboneRouterDomainPrefixCallback mDomainPrefixCallback;
     void *                               mDomainPrefixCallbackContext;
-
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-    bool mSkipSeqNumIncrease : 1;
-#endif
 };
 
 } // namespace BackboneRouter
diff --git a/src/core/backbone_router/bbr_manager.cpp b/src/core/backbone_router/bbr_manager.cpp
index 405d12e..850245d 100644
--- a/src/core/backbone_router/bbr_manager.cpp
+++ b/src/core/backbone_router/bbr_manager.cpp
@@ -39,7 +39,7 @@
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "thread/mle_types.hpp"
 #include "thread/thread_netif.hpp"
@@ -50,6 +50,8 @@
 
 namespace BackboneRouter {
 
+RegisterLogModule("BbrManager");
+
 Manager::Manager(Instance &aInstance)
     : InstanceLocator(aInstance)
 #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
@@ -108,11 +110,11 @@
 
             if (error != kErrorNone)
             {
-                otLogWarnBbr("Stop Backbone TMF agent: %s", ErrorToString(error));
+                LogWarn("Stop Backbone TMF agent: %s", ErrorToString(error));
             }
             else
             {
-                otLogInfoBbr("Stop Backbone TMF agent: %s", ErrorToString(error));
+                LogInfo("Stop Backbone TMF agent: %s", ErrorToString(error));
             }
         }
         else
@@ -226,7 +228,7 @@
 
             if (timeout != origTimeout)
             {
-                otLogNoteBbr("MLR.req: MLR timeout is normalized from %u to %u", origTimeout, timeout);
+                LogNote("MLR.req: MLR timeout is normalized from %u to %u", origTimeout, timeout);
             }
         }
     }
@@ -300,13 +302,11 @@
                                                         Ip6::Address *             aFailedAddresses,
                                                         uint8_t                    aFailedAddressNum)
 {
-    Error          error   = kErrorNone;
-    Coap::Message *message = nullptr;
+    Error          error = kErrorNone;
+    Coap::Message *message;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(message->SetDefaultResponseHeader(aMessage));
-    SuccessOrExit(message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewResponseMessage(aMessage);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(Tlv::Append<ThreadStatusTlv>(*message, aStatus));
 
@@ -328,7 +328,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    otLogInfoBbr("Sent MLR.rsp (status=%d): %s", aStatus, ErrorToString(error));
+    LogInfo("Sent MLR.rsp (status=%d): %s", aStatus, ErrorToString(error));
 }
 
 void Manager::SendBackboneMulticastListenerRegistration(const Ip6::Address *aAddresses,
@@ -343,10 +343,8 @@
 
     OT_ASSERT(aAddressNum >= kIp6AddressesNumMin && aAddressNum <= kIp6AddressesNumMax);
 
-    VerifyOrExit((message = backboneTmf.NewMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsNonConfirmablePost(UriPath::kBackboneMlr));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = backboneTmf.NewNonConfirmablePostMessage(UriPath::kBackboneMlr);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     addressesTlv.Init();
     addressesTlv.SetLength(sizeof(Ip6::Address) * aAddressNum);
@@ -365,7 +363,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    otLogInfoBbr("Sent BMLR.ntf: %s", ErrorToString(error));
+    LogInfo("Sent BMLR.ntf: %s", ErrorToString(error));
 }
 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
 
@@ -430,7 +428,7 @@
     }
 
 exit:
-    otLogInfoBbr("Received DUA.req on %s: %s", (isPrimary ? "PBBR" : "SBBR"), ErrorToString(error));
+    LogInfo("Received DUA.req on %s: %s", (isPrimary ? "PBBR" : "SBBR"), ErrorToString(error));
 
     if (error == kErrorNone)
     {
@@ -452,13 +450,11 @@
                                           const Ip6::Address &       aTarget,
                                           ThreadStatusTlv::DuaStatus aStatus)
 {
-    Error          error   = kErrorNone;
-    Coap::Message *message = nullptr;
+    Error          error = kErrorNone;
+    Coap::Message *message;
 
-    VerifyOrExit((message = Get<Tmf::Agent>().NewMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(message->SetDefaultResponseHeader(aMessage));
-    SuccessOrExit(message->SetPayloadMarker());
+    message = Get<Tmf::Agent>().NewResponseMessage(aMessage);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(Tlv::Append<ThreadStatusTlv>(*message, aStatus));
     SuccessOrExit(Tlv::Append<ThreadTargetTlv>(*message, aTarget));
@@ -467,8 +463,7 @@
 
 exit:
     FreeMessageOnError(message, error);
-    otLogInfoBbr("Sent DUA.rsp for DUA %s, status %d %s", aTarget.ToString().AsCString(), aStatus,
-                 ErrorToString(error));
+    LogInfo("Sent DUA.rsp for DUA %s, status %d %s", aTarget.ToString().AsCString(), aStatus, ErrorToString(error));
 }
 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
 
@@ -534,10 +529,8 @@
 
     VerifyOrExit(Get<Local>().IsPrimary(), error = kErrorInvalidState);
 
-    VerifyOrExit((message = mBackboneTmfAgent.NewPriorityMessage()) != nullptr, error = kErrorNoBufs);
-
-    SuccessOrExit(error = message->InitAsNonConfirmablePost(UriPath::kBackboneQuery));
-    SuccessOrExit(error = message->SetPayloadMarker());
+    message = mBackboneTmfAgent.NewPriorityNonConfirmablePostMessage(UriPath::kBackboneQuery);
+    VerifyOrExit(message != nullptr, error = kErrorNoBufs);
 
     SuccessOrExit(error = Tlv::Append<ThreadTargetTlv>(*message, aDua));
 
@@ -555,8 +548,7 @@
     error = mBackboneTmfAgent.SendMessage(*message, messageInfo);
 
 exit:
-    otLogInfoBbr("SendBackboneQuery for %s (rloc16=%04x): %s", aDua.ToString().AsCString(), aRloc16,
-                 ErrorToString(error));
+    LogInfo("SendBackboneQuery for %s (rloc16=%04x): %s", aDua.ToString().AsCString(), aRloc16, ErrorToString(error));
     FreeMessageOnError(message, error);
     return error;
 }
@@ -583,8 +575,8 @@
     error = Tlv::Find<ThreadRloc16Tlv>(aMessage, rloc16);
     VerifyOrExit(error == kErrorNone || error == kErrorNotFound);
 
-    otLogInfoBbr("Received BB.qry from %s for %s (rloc16=%04x)", aMessageInfo.GetPeerAddr().ToString().AsCString(),
-                 dua.ToString().AsCString(), rloc16);
+    LogInfo("Received BB.qry from %s for %s (rloc16=%04x)", aMessageInfo.GetPeerAddr().ToString().AsCString(),
+            dua.ToString().AsCString(), rloc16);
 
     ndProxy = mNdProxyTable.ResolveDua(dua);
     VerifyOrExit(ndProxy != nullptr && !ndProxy->GetDadFlag(), error = kErrorNotFound);
@@ -592,7 +584,7 @@
     error = SendBackboneAnswer(aMessageInfo, dua, rloc16, *ndProxy);
 
 exit:
-    otLogInfoBbr("HandleBackboneQuery: %s", ErrorToString(error));
+    LogInfo("HandleBackboneQuery: %s", ErrorToString(error));
 }
 
 void Manager::HandleBackboneAnswer(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
@@ -643,7 +635,7 @@
     SuccessOrExit(error = mBackboneTmfAgent.SendEmptyAck(aMessage, aMessageInfo));
 
 exit:
-    otLogInfoBbr("HandleBackboneAnswer: %s", ErrorToString(error));
+    LogInfo("HandleBackboneAnswer: %s", ErrorToString(error));
 }
 
 Error Manager::SendProactiveBackboneNotification(const Ip6::Address &            aDua,
@@ -687,7 +679,7 @@
     SuccessOrExit(error = Tlv::Append<ThreadLastTransactionTimeTlv>(*message, aTimeSinceLastTransaction));
 
     {
-        const Mac::NameData nameData = Get<Mac::Mac>().GetNetworkName().GetAsData();
+        const MeshCoP::NameData nameData = Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsData();
 
         SuccessOrExit(error = Tlv::Append<ThreadNetworkNameTlv>(*message, nameData.GetBuffer(), nameData.GetLength()));
     }
@@ -706,8 +698,8 @@
     error = mBackboneTmfAgent.SendMessage(*message, messageInfo);
 
 exit:
-    otLogInfoBbr("Send %s for %s (rloc16=%04x): %s", proactive ? "PRO_BB.ntf" : "BB.ans", aDua.ToString().AsCString(),
-                 aSrcRloc16, ErrorToString(error));
+    LogInfo("Send %s for %s (rloc16=%04x): %s", proactive ? "PRO_BB.ntf" : "BB.ans", aDua.ToString().AsCString(),
+            aSrcRloc16, ErrorToString(error));
 
     FreeMessageOnError(message, error);
     return error;
@@ -736,8 +728,8 @@
     ot::BackboneRouter::NdProxyTable::NotifyDadComplete(*ndProxy, duplicate);
 
 exit:
-    otLogInfoBbr("HandleDadBackboneAnswer: %s, target=%s, mliid=%s, duplicate=%s", ErrorToString(error),
-                 aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), duplicate ? "Y" : "N");
+    LogInfo("HandleDadBackboneAnswer: %s, target=%s, mliid=%s, duplicate=%s", ErrorToString(error),
+            aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), duplicate ? "Y" : "N");
 }
 
 void Manager::HandleExtendedBackboneAnswer(const Ip6::Address &            aDua,
@@ -750,9 +742,8 @@
     dest.SetToRoutingLocator(Get<Mle::MleRouter>().GetMeshLocalPrefix(), aSrcRloc16);
     Get<AddressResolver>().SendAddressQueryResponse(aDua, aMeshLocalIid, &aTimeSinceLastTransaction, dest);
 
-    otLogInfoBbr("HandleExtendedBackboneAnswer: target=%s, mliid=%s, LTT=%lds, rloc16=%04x",
-                 aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), aTimeSinceLastTransaction,
-                 aSrcRloc16);
+    LogInfo("HandleExtendedBackboneAnswer: target=%s, mliid=%s, LTT=%lds, rloc16=%04x", aDua.ToString().AsCString(),
+            aMeshLocalIid.ToString().AsCString(), aTimeSinceLastTransaction, aSrcRloc16);
 }
 
 void Manager::HandleProactiveBackboneNotification(const Ip6::Address &            aDua,
@@ -788,8 +779,8 @@
     }
 
 exit:
-    otLogInfoBbr("HandleProactiveBackboneNotification: %s, target=%s, mliid=%s, LTT=%lds", ErrorToString(error),
-                 aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), aTimeSinceLastTransaction);
+    LogInfo("HandleProactiveBackboneNotification: %s, target=%s, mliid=%s, LTT=%lds", ErrorToString(error),
+            aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), aTimeSinceLastTransaction);
 }
 #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
 
@@ -799,11 +790,11 @@
 
     if (aError == kErrorNone)
     {
-        otLogInfoBbr("%s: %s", aText, ErrorToString(aError));
+        LogInfo("%s: %s", aText, ErrorToString(aError));
     }
     else
     {
-        otLogWarnBbr("%s: %s", aText, ErrorToString(aError));
+        LogWarn("%s: %s", aText, ErrorToString(aError));
     }
 }
 
diff --git a/src/core/backbone_router/multicast_listeners_table.cpp b/src/core/backbone_router/multicast_listeners_table.cpp
index bf9b51e..35a4972 100644
--- a/src/core/backbone_router/multicast_listeners_table.cpp
+++ b/src/core/backbone_router/multicast_listeners_table.cpp
@@ -35,10 +35,11 @@
 
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
 
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "thread/mle_types.hpp"
 #include "thread/thread_netif.hpp"
@@ -48,6 +49,8 @@
 
 namespace BackboneRouter {
 
+RegisterLogModule("BbrMlt");
+
 Error MulticastListenersTable::Add(const Ip6::Address &aAddress, Time aExpireTime)
 {
     Error error = kErrorNone;
@@ -66,7 +69,7 @@
         }
     }
 
-    VerifyOrExit(mNumValidListeners < OT_ARRAY_LENGTH(mListeners), error = kErrorNoBufs);
+    VerifyOrExit(mNumValidListeners < GetArrayLength(mListeners), error = kErrorNoBufs);
 
     mListeners[mNumValidListeners].SetAddress(aAddress);
     mListeners[mNumValidListeners].SetExpireTime(aExpireTime);
@@ -154,8 +157,8 @@
     OT_UNUSED_VARIABLE(aExpireTime);
     OT_UNUSED_VARIABLE(aError);
 
-    otLogDebgBbr("MulticastListenersTable: %s %s expire %u: %s", aAction, aAddress.ToString().AsCString(),
-                 aExpireTime.GetValue(), ErrorToString(aError));
+    LogDebg("%s %s expire %u: %s", aAction, aAddress.ToString().AsCString(), aExpireTime.GetValue(),
+            ErrorToString(aError));
 }
 
 void MulticastListenersTable::FixHeap(uint16_t aIndex)
diff --git a/src/core/backbone_router/ndproxy_table.cpp b/src/core/backbone_router/ndproxy_table.cpp
index f680daa..9d65958 100644
--- a/src/core/backbone_router/ndproxy_table.cpp
+++ b/src/core/backbone_router/ndproxy_table.cpp
@@ -35,13 +35,16 @@
 
 #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
 
+#include "common/array.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 
 namespace ot {
 
 namespace BackboneRouter {
 
+RegisterLogModule("BbrNdProxy");
+
 void NdProxyTable::NdProxy::Init(const Ip6::InterfaceIdentifier &aAddressIid,
                                  const Ip6::InterfaceIdentifier &aMeshLocalIid,
                                  uint16_t                        aRloc16,
@@ -107,7 +110,7 @@
     : InstanceLocator(aInstance)
 {
     NdProxyTable &table = GetInstance().Get<BackboneRouter::NdProxyTable>();
-    mItem               = OT_ARRAY_END(table.mProxies);
+    mItem               = GetArrayEnd(table.mProxies);
 }
 
 void NdProxyTable::Iterator::Advance(void)
@@ -117,7 +120,7 @@
     do
     {
         mItem++;
-    } while (mItem < OT_ARRAY_END(table.mProxies) && !MatchesFilter(*mItem, mFilter));
+    } while (mItem < GetArrayEnd(table.mProxies) && !MatchesFilter(*mItem, mFilter));
 }
 
 void NdProxyTable::Erase(NdProxy &aNdProxy)
@@ -146,7 +149,7 @@
         mCallback(mCallbackContext, OT_BACKBONE_ROUTER_NDPROXY_CLEARED, nullptr);
     }
 
-    otLogNoteBbr("NdProxyTable::Clear!");
+    LogInfo("NdProxyTable::Clear!");
 }
 
 Error NdProxyTable::Register(const Ip6::InterfaceIdentifier &aAddressIid,
@@ -185,8 +188,8 @@
     mIsAnyDadInProcess = true;
 
 exit:
-    otLogInfoBbr("NdProxyTable::Register %s MLIID %s RLOC16 %04x LTT %u => %s", aAddressIid.ToString().AsCString(),
-                 aMeshLocalIid.ToString().AsCString(), aRloc16, timeSinceLastTransaction, ErrorToString(error));
+    LogInfo("NdProxyTable::Register %s MLIID %s RLOC16 %04x LTT %u => %s", aAddressIid.ToString().AsCString(),
+            aMeshLocalIid.ToString().AsCString(), aRloc16, timeSinceLastTransaction, ErrorToString(error));
     return error;
 }
 
@@ -203,8 +206,8 @@
     }
 
 exit:
-    otLogDebgBbr("NdProxyTable::FindByAddressIid(%s) => %s", aAddressIid.ToString().AsCString(),
-                 found ? found->mMeshLocalIid.ToString().AsCString() : "NOT_FOUND");
+    LogDebg("NdProxyTable::FindByAddressIid(%s) => %s", aAddressIid.ToString().AsCString(),
+            found ? found->mMeshLocalIid.ToString().AsCString() : "NOT_FOUND");
     return found;
 }
 
@@ -221,8 +224,8 @@
     }
 
 exit:
-    otLogDebgBbr("NdProxyTable::FindByMeshLocalIid(%s) => %s", aMeshLocalIid.ToString().AsCString(),
-                 found ? found->mAddressIid.ToString().AsCString() : "NOT_FOUND");
+    LogDebg("NdProxyTable::FindByMeshLocalIid(%s) => %s", aMeshLocalIid.ToString().AsCString(),
+            found ? found->mAddressIid.ToString().AsCString() : "NOT_FOUND");
     return found;
 }
 
@@ -236,7 +239,7 @@
     }
 
 exit:
-    otLogDebgBbr("NdProxyTable::FindInvalid() => %s", found ? "OK" : "NOT_FOUND");
+    LogDebg("NdProxyTable::FindInvalid() => %s", found ? "OK" : "NOT_FOUND");
     return found;
 }
 
diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp
index f861681..44ce6a0 100644
--- a/src/core/border_router/routing_manager.cpp
+++ b/src/core/border_router/routing_manager.cpp
@@ -44,7 +44,7 @@
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "common/settings.hpp"
 #include "net/ip6.hpp"
@@ -56,6 +56,8 @@
 
 namespace BorderRouter {
 
+RegisterLogModule("BorderRouter");
+
 RoutingManager::RoutingManager(Instance &aInstance)
     : InstanceLocator(aInstance)
     , mIsRunning(false)
@@ -70,6 +72,7 @@
     , mDiscoveredPrefixInvalidTimer(aInstance, HandleDiscoveredPrefixInvalidTimer)
     , mDiscoveredPrefixStaleTimer(aInstance, HandleDiscoveredPrefixStaleTimer)
     , mRouterAdvertisementCount(0)
+    , mLastRouterAdvertisementSendTime(TimerMilli::GetNow() - kMinDelayBetweenRtrAdvs)
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE
     , mVicariousRouterSolicitTimer(aInstance, HandleVicariousRouterSolicitTimer)
 #endif
@@ -77,6 +80,8 @@
     , mRouterSolicitCount(0)
     , mRoutingPolicyTimer(aInstance, HandleRoutingPolicyTimer)
 {
+    mBrUlaPrefix.Clear();
+
     mLocalOmrPrefix.Clear();
 
     mLocalOnLinkPrefix.Clear();
@@ -91,11 +96,12 @@
     VerifyOrExit(!IsInitialized(), error = kErrorInvalidState);
     VerifyOrExit(aInfraIfIndex > 0, error = kErrorInvalidArgs);
 
-    SuccessOrExit(error = LoadOrGenerateRandomOmrPrefix());
-    SuccessOrExit(error = LoadOrGenerateRandomOnLinkPrefix());
+    SuccessOrExit(error = LoadOrGenerateRandomBrUlaPrefix());
+    GenerateOmrPrefix();
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
-    SuccessOrExit(error = LoadOrGenerateRandomNat64Prefix());
+    GenerateNat64Prefix();
 #endif
+    SuccessOrExit(error = LoadOrGenerateRandomOnLinkPrefix());
 
     mInfraIfIndex = aInfraIfIndex;
 
@@ -160,94 +166,95 @@
 }
 #endif
 
-Error RoutingManager::LoadOrGenerateRandomOmrPrefix(void)
+Error RoutingManager::LoadOrGenerateRandomBrUlaPrefix(void)
 {
-    Error error = kErrorNone;
+    Error error     = kErrorNone;
+    bool  generated = false;
 
-    if (Get<Settings>().Read<Settings::OmrPrefix>(mLocalOmrPrefix) != kErrorNone || !IsValidOmrPrefix(mLocalOmrPrefix))
+    if (Get<Settings>().Read<Settings::BrUlaPrefix>(mBrUlaPrefix) != kErrorNone || !IsValidBrUlaPrefix(mBrUlaPrefix))
     {
-        Ip6::NetworkPrefix randomOmrPrefix;
+        Ip6::NetworkPrefix randomUlaPrefix;
 
-        otLogNoteBr("No valid OMR prefix found in settings, generating new one");
+        LogNote("No valid /48 BR ULA prefix found in settings, generating new one");
 
-        // TODO: generate OMR prefix from the /48 BR ULA prefix
-        error = randomOmrPrefix.GenerateRandomUla();
-        if (error != kErrorNone)
-        {
-            otLogCritBr("Failed to generate random OMR prefix");
-            ExitNow();
-        }
+        SuccessOrExit(error = randomUlaPrefix.GenerateRandomUla());
 
-        mLocalOmrPrefix.Set(randomOmrPrefix);
-        IgnoreError(Get<Settings>().Save<Settings::OmrPrefix>(mLocalOmrPrefix));
+        mBrUlaPrefix.Set(randomUlaPrefix);
+        mBrUlaPrefix.SetSubnetId(0);
+        mBrUlaPrefix.SetLength(kBrUlaPrefixLength);
+
+        IgnoreError(Get<Settings>().Save<Settings::BrUlaPrefix>(mBrUlaPrefix));
+        generated = true;
     }
 
+    OT_UNUSED_VARIABLE(generated);
+
+    LogNote("BR ULA prefix: %s (%s)", mBrUlaPrefix.ToString().AsCString(), generated ? "generated" : "loaded");
+
 exit:
+    if (error != kErrorNone)
+    {
+        LogCrit("Failed to generate random /48 BR ULA prefix");
+    }
     return error;
 }
 
+void RoutingManager::GenerateOmrPrefix(void)
+{
+    IgnoreError(Get<Settings>().Delete<Settings::LegacyOmrPrefix>());
+
+    mLocalOmrPrefix = mBrUlaPrefix;
+    mLocalOmrPrefix.SetSubnetId(kOmrPrefixSubnetId);
+    mLocalOmrPrefix.SetLength(kOmrPrefixLength);
+
+    LogInfo("Generated OMR prefix: %s", mLocalOmrPrefix.ToString().AsCString());
+}
+
+#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
+void RoutingManager::GenerateNat64Prefix(void)
+{
+    mLocalNat64Prefix = mBrUlaPrefix;
+    mLocalNat64Prefix.SetSubnetId(kNat64PrefixSubnetId);
+    mLocalNat64Prefix.mPrefix.mFields.m32[2] = 0;
+    mLocalNat64Prefix.SetLength(kNat64PrefixLength);
+
+    LogInfo("Generated NAT64 prefix: %s", mLocalNat64Prefix.ToString().AsCString());
+}
+#endif
+
 Error RoutingManager::LoadOrGenerateRandomOnLinkPrefix(void)
 {
-    Error error = kErrorNone;
+    Error error     = kErrorNone;
+    bool  generated = false;
 
     if (Get<Settings>().Read<Settings::OnLinkPrefix>(mLocalOnLinkPrefix) != kErrorNone ||
         !mLocalOnLinkPrefix.IsUniqueLocal())
     {
         Ip6::NetworkPrefix randomOnLinkPrefix;
 
-        otLogNoteBr("No valid on-link prefix found in settings, generating new one");
-
         error = randomOnLinkPrefix.GenerateRandomUla();
         if (error != kErrorNone)
         {
-            otLogCritBr("Failed to generate random on-link prefix");
+            LogCrit("Failed to generate random on-link prefix");
             ExitNow();
         }
 
-        randomOnLinkPrefix.m8[6] = 0;
-        randomOnLinkPrefix.m8[7] = 0;
         mLocalOnLinkPrefix.Set(randomOnLinkPrefix);
+        mLocalOnLinkPrefix.SetSubnetId(0);
 
         IgnoreError(Get<Settings>().Save<Settings::OnLinkPrefix>(mLocalOnLinkPrefix));
+        generated = true;
     }
 
+    OT_UNUSED_VARIABLE(generated);
+
+    LogNote("Local on-link prefix: %s (%s)", mLocalOnLinkPrefix.ToString().AsCString(),
+            generated ? "generated" : "loaded");
+
 exit:
     return error;
 }
 
-#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
-Error RoutingManager::LoadOrGenerateRandomNat64Prefix(void)
-{
-    Error error = kErrorNone;
-
-    if (Get<Settings>().Read<Settings::Nat64Prefix>(mLocalNat64Prefix) != kErrorNone ||
-        !mLocalNat64Prefix.IsValidNat64())
-    {
-        Ip6::NetworkPrefix randomNat64Prefix;
-        constexpr uint8_t  nat64PrefixLength = 96;
-
-        otLogNoteBr("No valid NAT64 prefix found in settings, generating new one");
-
-        // TODO: generate NAT64 prefix from the /48 BR ULA prefix
-        error = randomNat64Prefix.GenerateRandomUla();
-        if (error != kErrorNone)
-        {
-            otLogCritBr("Failed to generate random NAT64 prefix");
-            ExitNow();
-        }
-
-        mLocalNat64Prefix.Clear();
-        mLocalNat64Prefix.Set(randomNat64Prefix);
-        mLocalNat64Prefix.SetLength(nat64PrefixLength);
-
-        IgnoreError(Get<Settings>().Save<Settings::Nat64Prefix>(mLocalNat64Prefix));
-    }
-
-exit:
-    return error;
-}
-#endif
-
 void RoutingManager::EvaluateState(void)
 {
     if (mIsEnabled && Get<Mle::MleRouter>().IsAttached() && mInfraIfIsRunning)
@@ -264,7 +271,7 @@
 {
     if (!mIsRunning)
     {
-        otLogInfoBr("Border Routing manager started");
+        LogInfo("Border Routing manager started");
 
         mIsRunning = true;
         StartRouterSolicitationDelay();
@@ -315,7 +322,7 @@
 
     mRoutingPolicyTimer.Stop();
 
-    otLogInfoBr("Border Routing manager stopped");
+    LogInfo("Border Routing manager stopped");
 
     mIsRunning = false;
 
@@ -352,7 +359,7 @@
 exit:
     if (error != kErrorNone)
     {
-        otLogDebgBr("Dropped ICMPv6 message: %s", ErrorToString(error));
+        LogDebg("Dropped ICMPv6 message: %s", ErrorToString(error));
     }
 }
 
@@ -364,8 +371,8 @@
     VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = kErrorInvalidArgs);
     VerifyOrExit(aIsRunning != mInfraIfIsRunning);
 
-    otLogInfoBr("Infra interface (%u) state changed: %sRUNNING -> %sRUNNING", aInfraIfIndex,
-                (mInfraIfIsRunning ? "" : "NOT "), (aIsRunning ? "" : "NOT "));
+    LogInfo("Infra interface (%u) state changed: %sRUNNING -> %sRUNNING", aInfraIfIndex,
+            (mInfraIfIsRunning ? "" : "NOT "), (aIsRunning ? "" : "NOT "));
 
     mInfraIfIsRunning = aIsRunning;
     EvaluateState();
@@ -420,7 +427,7 @@
 
         if (aNewOmrPrefixes.PushBack(prefix) != kErrorNone)
         {
-            otLogWarnBr("EvaluateOmrPrefix: Too many OMR prefixes, ignoring prefix %s", prefix.ToString().AsCString());
+            LogWarn("EvaluateOmrPrefix: Too many OMR prefixes, ignoring prefix %s", prefix.ToString().AsCString());
             continue;
         }
 
@@ -439,7 +446,7 @@
 
     if (aNewOmrPrefixes.IsEmpty())
     {
-        otLogInfoBr("EvaluateOmrPrefix: No valid OMR prefixes found in Thread network");
+        LogInfo("EvaluateOmrPrefix: No valid OMR prefixes found in Thread network");
 
         if (PublishLocalOmrPrefix() == kErrorNone)
         {
@@ -449,18 +456,29 @@
         // The `aNewOmrPrefixes` remains empty if we fail to publish
         // the local OMR prefix.
     }
-    else if (publishedLocalOmrPrefix != nullptr && smallestOmrPrefix != publishedLocalOmrPrefix)
+    else
     {
-        otLogInfoBr("EvaluateOmrPrefix: There is already a smaller OMR prefix %s in the Thread network",
+        OT_ASSERT(smallestOmrPrefix != nullptr);
+
+        if (*smallestOmrPrefix == mLocalOmrPrefix)
+        {
+            IgnoreError(PublishLocalOmrPrefix());
+        }
+        else if (IsOmrPrefixAddedToLocalNetworkData())
+        {
+            LogInfo("EvaluateOmrPrefix: There is already a smaller OMR prefix %s in the Thread network",
                     smallestOmrPrefix->ToString().AsCString());
 
-        UnpublishLocalOmrPrefix();
+            UnpublishLocalOmrPrefix();
 
-        // Remove the local OMR prefix from the list by overwriting it
-        // with the last element and then popping it from the list.
-
-        *publishedLocalOmrPrefix = *aNewOmrPrefixes.Back();
-        aNewOmrPrefixes.PopBack();
+            // Remove the local OMR prefix from the list by overwriting it
+            // with the last element and then popping it from the list.
+            if (publishedLocalOmrPrefix != nullptr)
+            {
+                *publishedLocalOmrPrefix = *aNewOmrPrefixes.Back();
+                aNewOmrPrefixes.PopBack();
+            }
+        }
     }
 }
 
@@ -471,6 +489,8 @@
 
     OT_ASSERT(mIsRunning);
 
+    VerifyOrExit(!IsOmrPrefixAddedToLocalNetworkData());
+
     omrPrefixConfig.Clear();
     omrPrefixConfig.mPrefix       = mLocalOmrPrefix;
     omrPrefixConfig.mStable       = true;
@@ -483,15 +503,16 @@
     error = Get<NetworkData::Local>().AddOnMeshPrefix(omrPrefixConfig);
     if (error != kErrorNone)
     {
-        otLogWarnBr("Failed to publish local OMR prefix %s in Thread network: %s",
-                    mLocalOmrPrefix.ToString().AsCString(), ErrorToString(error));
+        LogWarn("Failed to publish local OMR prefix %s in Thread network: %s", mLocalOmrPrefix.ToString().AsCString(),
+                ErrorToString(error));
     }
     else
     {
         Get<NetworkData::Notifier>().HandleServerDataUpdated();
-        otLogInfoBr("Publishing local OMR prefix %s in Thread network", mLocalOmrPrefix.ToString().AsCString());
+        LogInfo("Publishing local OMR prefix %s in Thread network", mLocalOmrPrefix.ToString().AsCString());
     }
 
+exit:
     return error;
 }
 
@@ -501,19 +522,26 @@
 
     VerifyOrExit(mIsRunning);
 
+    VerifyOrExit(IsOmrPrefixAddedToLocalNetworkData());
+
     SuccessOrExit(error = Get<NetworkData::Local>().RemoveOnMeshPrefix(mLocalOmrPrefix));
 
     Get<NetworkData::Notifier>().HandleServerDataUpdated();
-    otLogInfoBr("Unpublishing local OMR prefix %s from Thread network", mLocalOmrPrefix.ToString().AsCString());
+    LogInfo("Unpublishing local OMR prefix %s from Thread network", mLocalOmrPrefix.ToString().AsCString());
 
 exit:
-    if (error != kErrorNone)
+    if (error != kErrorNone && error != kErrorNotFound)
     {
-        otLogWarnBr("Failed to unpublish local OMR prefix %s from Thread network: %s",
-                    mLocalOmrPrefix.ToString().AsCString(), ErrorToString(error));
+        LogWarn("Failed to unpublish local OMR prefix %s from Thread network: %s",
+                mLocalOmrPrefix.ToString().AsCString(), ErrorToString(error));
     }
 }
 
+bool RoutingManager::IsOmrPrefixAddedToLocalNetworkData(void) const
+{
+    return Get<NetworkData::Local>().ContainsOnMeshPrefix(mLocalOmrPrefix);
+}
+
 Error RoutingManager::AddExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64)
 {
     Error                            error;
@@ -530,12 +558,12 @@
     error = Get<NetworkData::Local>().AddHasRoutePrefix(routeConfig);
     if (error != kErrorNone)
     {
-        otLogWarnBr("Failed to add external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error));
+        LogWarn("Failed to add external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error));
     }
     else
     {
         Get<NetworkData::Notifier>().HandleServerDataUpdated();
-        otLogInfoBr("Adding external route %s", aPrefix.ToString().AsCString());
+        LogInfo("Adding external route %s", aPrefix.ToString().AsCString());
     }
 
     return error;
@@ -550,12 +578,12 @@
     SuccessOrExit(error = Get<NetworkData::Local>().RemoveHasRoutePrefix(aPrefix));
 
     Get<NetworkData::Notifier>().HandleServerDataUpdated();
-    otLogInfoBr("Removing external route %s", aPrefix.ToString().AsCString());
+    LogInfo("Removing external route %s", aPrefix.ToString().AsCString());
 
 exit:
     if (error != kErrorNone)
     {
-        otLogWarnBr("Failed to remove external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error));
+        LogWarn("Failed to remove external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error));
     }
 }
 
@@ -604,8 +632,8 @@
         }
         else
         {
-            otLogInfoBr("EvaluateOnLinkPrefix: There is already smaller on-link prefix %s on interface %u",
-                        smallestOnLinkPrefix->ToString().AsCString(), mInfraIfIndex);
+            LogInfo("EvaluateOnLinkPrefix: There is already smaller on-link prefix %s on interface %u",
+                    smallestOnLinkPrefix->ToString().AsCString(), mInfraIfIndex);
             DeprecateOnLinkPrefix();
         }
     }
@@ -621,7 +649,7 @@
 
 void RoutingManager::HandleOnLinkPrefixDeprecateTimer(void)
 {
-    otLogInfoBr("Local on-link prefix %s expired", mLocalOnLinkPrefix.ToString().AsCString());
+    LogInfo("Local on-link prefix %s expired", mLocalOnLinkPrefix.ToString().AsCString());
     RemoveExternalRoute(mLocalOnLinkPrefix);
 }
 
@@ -629,7 +657,7 @@
 {
     OT_ASSERT(mIsAdvertisingLocalOnLinkPrefix);
 
-    otLogInfoBr("Deprecate local on-link prefix %s", mLocalOnLinkPrefix.ToString().AsCString());
+    LogInfo("Deprecate local on-link prefix %s", mLocalOnLinkPrefix.ToString().AsCString());
     mOnLinkPrefixDeprecateTimer.StartAt(mTimeAdvertisedOnLinkPrefix,
                                         TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime));
 }
@@ -643,7 +671,7 @@
     NetworkData::ExternalRouteConfig config;
     Ip6::Prefix                      smallestNat64Prefix;
 
-    otLogInfoBr("Evaluating NAT64 prefix");
+    LogInfo("Evaluating NAT64 prefix");
 
     smallestNat64Prefix.Clear();
     while (Get<NetworkData::Leader>().GetNextExternalRoute(iterator, config) == kErrorNone)
@@ -661,8 +689,8 @@
 
     if (smallestNat64Prefix.GetLength() == 0 || smallestNat64Prefix == mLocalNat64Prefix)
     {
-        otLogInfoBr("No NAT64 prefix in Network Data is smaller than the local NAT64 prefix %s",
-                    mLocalNat64Prefix.ToString().AsCString());
+        LogInfo("No NAT64 prefix in Network Data is smaller than the local NAT64 prefix %s",
+                mLocalNat64Prefix.ToString().AsCString());
 
         // Advertise local NAT64 prefix.
         if (!mIsAdvertisingLocalNat64Prefix &&
@@ -675,8 +703,8 @@
     {
         // Withdraw local NAT64 prefix if it's not the smallest one in Network Data.
         // TODO: remove the prefix with lower preference after discovering upstream NAT64 prefix is supported
-        otLogNoteBr("Withdrawing local NAT64 prefix since a smaller one %s exists.",
-                    smallestNat64Prefix.ToString().AsCString());
+        LogNote("Withdrawing local NAT64 prefix since a smaller one %s exists.",
+                smallestNat64Prefix.ToString().AsCString());
 
         RemoveExternalRoute(mLocalNat64Prefix);
         mIsAdvertisingLocalNat64Prefix = false;
@@ -687,7 +715,7 @@
 // This method evaluate the routing policy depends on prefix and route
 // information on Thread Network and infra link. As a result, this
 // method May send RA messages on infra link and publish/unpublish
-// OMR prefix in the Thread network.
+// OMR and NAT64 prefix in the Thread network.
 void RoutingManager::EvaluateRoutingPolicy(void)
 {
     OT_ASSERT(mIsRunning);
@@ -695,9 +723,9 @@
     const Ip6::Prefix *newOnLinkPrefix = nullptr;
     OmrPrefixArray     newOmrPrefixes;
 
-    otLogInfoBr("Evaluating routing policy");
+    LogInfo("Evaluating routing policy");
 
-    // 0. Evaluate on-link & OMR prefixes.
+    // 0. Evaluate on-link, OMR and NAT64 prefixes.
     newOnLinkPrefix = EvaluateOnLinkPrefix();
     EvaluateOmrPrefix(newOmrPrefixes);
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
@@ -710,13 +738,13 @@
     if (newOmrPrefixes.IsEmpty())
     {
         // This is the very exceptional case and happens only when we failed to publish
-        // our local OMR prefix to the Thread network. We schedule the Router Advertisement
+        // our local OMR prefix to the Thread network. We schedule the routing policy
         // timer to re-evaluate our routing policy in the future.
 
-        otLogWarnBr("No OMR prefix advertised! Start Router Advertisement timer for future evaluation");
+        LogWarn("No OMR prefix advertised! Start Routing Policy timer for future evaluation");
     }
 
-    // 2. Schedule Router Advertisement timer with random interval.
+    // 2. Schedule routing policy timer with random interval for the next Router Advertisement.
     {
         uint32_t nextSendDelay;
 
@@ -727,7 +755,6 @@
             nextSendDelay = kMaxInitRtrAdvInterval;
         }
 
-        otLogInfoBr("Router advertisement scheduled in %u seconds", nextSendDelay);
         StartRoutingPolicyEvaluationDelay(Time::SecToMsec(nextSendDelay));
     }
 
@@ -745,8 +772,15 @@
 
 void RoutingManager::StartRoutingPolicyEvaluationDelay(uint32_t aDelayMilli)
 {
-    otLogInfoBr("Start evaluating routing policy, scheduled in %u milliseconds", aDelayMilli);
-    mRoutingPolicyTimer.FireAtIfEarlier(TimerMilli::GetNow() + aDelayMilli);
+    TimeMilli now          = TimerMilli::GetNow();
+    TimeMilli evaluateTime = now + aDelayMilli;
+    TimeMilli earlestTime  = mLastRouterAdvertisementSendTime + kMinDelayBetweenRtrAdvs;
+
+    evaluateTime = OT_MAX(evaluateTime, earlestTime);
+
+    LogInfo("Start evaluating routing policy, scheduled in %u milliseconds", evaluateTime - now);
+
+    mRoutingPolicyTimer.FireAtIfEarlier(evaluateTime);
 }
 
 // starts sending Router Solicitations in random delay
@@ -766,7 +800,7 @@
     static_assert(kMaxRtrSolicitationDelay > 0, "invalid maximum Router Solicitation delay");
     randomDelay = Random::NonCrypto::GetUint32InRange(0, Time::SecToMsec(kMaxRtrSolicitationDelay));
 
-    otLogInfoBr("Start Router Solicitation, scheduled in %u milliseconds", randomDelay);
+    LogInfo("Start Router Solicitation, scheduled in %u milliseconds", randomDelay);
     mTimeRouterSolicitStart = TimerMilli::GetNow();
     mRouterSolicitTimer.Start(randomDelay);
 
@@ -823,12 +857,12 @@
 
         if (!mIsAdvertisingLocalOnLinkPrefix)
         {
-            otLogInfoBr("Start advertising new on-link prefix %s on interface %u",
-                        aNewOnLinkPrefix->ToString().AsCString(), mInfraIfIndex);
+            LogInfo("Start advertising new on-link prefix %s on interface %u", aNewOnLinkPrefix->ToString().AsCString(),
+                    mInfraIfIndex);
         }
 
-        otLogInfoBr("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)",
-                    aNewOnLinkPrefix->ToString().AsCString(), pio.GetPreferredLifetime(), pio.GetValidLifetime());
+        LogInfo("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)",
+                aNewOnLinkPrefix->ToString().AsCString(), pio.GetPreferredLifetime(), pio.GetValidLifetime());
 
         mTimeAdvertisedOnLinkPrefix = TimerMilli::GetNow();
     }
@@ -848,8 +882,8 @@
         memcpy(buffer + bufferLength, &pio, pio.GetSize());
         bufferLength += pio.GetSize();
 
-        otLogInfoBr("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)",
-                    mLocalOnLinkPrefix.ToString().AsCString(), pio.GetPreferredLifetime(), pio.GetValidLifetime());
+        LogInfo("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)",
+                mLocalOnLinkPrefix.ToString().AsCString(), pio.GetPreferredLifetime(), pio.GetValidLifetime());
     }
 
     // Invalidate the advertised OMR prefixes if they are no longer in the new OMR prefix array.
@@ -868,8 +902,8 @@
             memcpy(buffer + bufferLength, &rio, rio.GetSize());
             bufferLength += rio.GetSize();
 
-            otLogInfoBr("Stop advertising OMR prefix %s on interface %u", advertisedOmrPrefix.ToString().AsCString(),
-                        mInfraIfIndex);
+            LogInfo("Stop advertising OMR prefix %s on interface %u", advertisedOmrPrefix.ToString().AsCString(),
+                    mInfraIfIndex);
         }
     }
 
@@ -884,8 +918,8 @@
         memcpy(buffer + bufferLength, &rio, rio.GetSize());
         bufferLength += rio.GetSize();
 
-        otLogInfoBr("Send OMR prefix %s in RIO (valid lifetime = %u seconds)", newOmrPrefix.ToString().AsCString(),
-                    kDefaultOmrPrefixLifetime);
+        LogInfo("Send OMR prefix %s in RIO (valid lifetime = %u seconds)", newOmrPrefix.ToString().AsCString(),
+                kDefaultOmrPrefixLifetime);
     }
 
     // Send the message only when there are options.
@@ -901,16 +935,22 @@
 
         if (error == kErrorNone)
         {
-            otLogInfoBr("Sent Router Advertisement on interface %u", mInfraIfIndex);
-            otDumpDebgBr("[BR-CERT] direction=send | type=RA |", buffer, bufferLength);
+            mLastRouterAdvertisementSendTime = TimerMilli::GetNow();
+            LogInfo("Sent Router Advertisement on interface %u", mInfraIfIndex);
+            DumpDebg("[BR-CERT] direction=send | type=RA |", buffer, bufferLength);
         }
         else
         {
-            otLogWarnBr("Failed to send Router Advertisement on interface %u: %s", mInfraIfIndex, ErrorToString(error));
+            LogWarn("Failed to send Router Advertisement on interface %u: %s", mInfraIfIndex, ErrorToString(error));
         }
     }
 }
 
+bool RoutingManager::IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix)
+{
+    return aBrUlaPrefix.mLength == kBrUlaPrefixLength && aBrUlaPrefix.mPrefix.mFields.m8[0] == 0xfd;
+}
+
 bool RoutingManager::IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig)
 {
     return IsValidOmrPrefix(aOnMeshPrefixConfig.GetPrefix()) && aOnMeshPrefixConfig.mSlaac && !aOnMeshPrefixConfig.mDp;
@@ -941,7 +981,7 @@
 
 void RoutingManager::HandleVicariousRouterSolicitTimer(void)
 {
-    otLogInfoBr("Vicarious router solicitation time out");
+    LogInfo("Vicarious router solicitation time out");
 
     for (const ExternalPrefix &prefix : mDiscoveredPrefixes)
     {
@@ -961,7 +1001,7 @@
 
 void RoutingManager::HandleRouterSolicitTimer(void)
 {
-    otLogInfoBr("Router solicitation times out");
+    LogInfo("Router solicitation times out");
 
     if (mRouterSolicitCount < kMaxRtrSolicitations)
     {
@@ -972,14 +1012,14 @@
 
         if (error == kErrorNone)
         {
-            otLogDebgBr("Successfully sent %uth Router Solicitation", mRouterSolicitCount);
+            LogDebg("Successfully sent %uth Router Solicitation", mRouterSolicitCount);
             ++mRouterSolicitCount;
             nextSolicitationDelay =
                 (mRouterSolicitCount == kMaxRtrSolicitations) ? kMaxRtrSolicitationDelay : kRtrSolicitationInterval;
         }
         else
         {
-            otLogCritBr("Failed to send %uth Router Solicitation: %s", mRouterSolicitCount, ErrorToString(error));
+            LogCrit("Failed to send %uth Router Solicitation: %s", mRouterSolicitCount, ErrorToString(error));
 
             // It's unexpected that RS will fail and we will retry sending RS messages in 60 seconds.
             // Notice that `mRouterSolicitCount` is not incremented for failed RS and thus we will
@@ -989,7 +1029,7 @@
             mRouterSolicitCount   = 0;
         }
 
-        otLogDebgBr("Router solicitation timer scheduled in %u seconds", nextSolicitationDelay);
+        LogDebg("Router solicitation timer scheduled in %u seconds", nextSolicitationDelay);
         mRouterSolicitTimer.Start(Time::SecToMsec(nextSolicitationDelay));
     }
     else
@@ -1033,7 +1073,7 @@
 
 void RoutingManager::HandleDiscoveredPrefixStaleTimer(void)
 {
-    otLogInfoBr("Stale On-Link or OMR Prefixes or RA messages are detected");
+    LogInfo("Stale On-Link or OMR Prefixes or RA messages are detected");
     StartRouterSolicitationDelay();
 }
 
@@ -1060,8 +1100,7 @@
     OT_UNUSED_VARIABLE(aBuffer);
     OT_UNUSED_VARIABLE(aBufferLength);
 
-    otLogInfoBr("Received Router Solicitation from %s on interface %u", aSrcAddress.ToString().AsCString(),
-                mInfraIfIndex);
+    LogInfo("Received Router Solicitation from %s on interface %u", aSrcAddress.ToString().AsCString(), mInfraIfIndex);
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE
     if (!mVicariousRouterSolicitTimer.IsRunning())
@@ -1071,7 +1110,7 @@
     }
 #endif
 
-    // Schedule Router Advertisements with random delay.
+    // Schedule routing policy evaluation with random jitter to respond with Router Advertisement.
     StartRoutingPolicyEvaluationJitter(kRaReplyJitter);
 }
 
@@ -1111,9 +1150,8 @@
 
     VerifyOrExit(aBufferLength >= sizeof(RouterAdvMessage));
 
-    otLogInfoBr("Received Router Advertisement from %s on interface %u", aSrcAddress.ToString().AsCString(),
-                mInfraIfIndex);
-    otDumpDebgBr("[BR-CERT] direction=recv | type=RA |", aBuffer, aBufferLength);
+    LogInfo("Received Router Advertisement from %s on interface %u", aSrcAddress.ToString().AsCString(), mInfraIfIndex);
+    DumpDebg("[BR-CERT] direction=recv | type=RA |", aBuffer, aBufferLength);
 
     routerAdvMessage = reinterpret_cast<const RouterAdvMessage *>(aBuffer);
     optionsBegin     = aBuffer + sizeof(RouterAdvMessage);
@@ -1179,14 +1217,14 @@
 
     if (!IsValidOnLinkPrefix(aPio))
     {
-        otLogInfoBr("Ignore invalid on-link prefix in PIO: %s", prefix.ToString().AsCString());
+        LogInfo("Ignore invalid on-link prefix in PIO: %s", prefix.ToString().AsCString());
         ExitNow();
     }
 
     VerifyOrExit(!mIsAdvertisingLocalOnLinkPrefix || prefix != mLocalOnLinkPrefix);
 
-    otLogInfoBr("Discovered on-link prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(),
-                aPio.GetValidLifetime(), mInfraIfIndex);
+    LogInfo("Discovered on-link prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(),
+            aPio.GetValidLifetime(), mInfraIfIndex);
 
     onLinkPrefix.mIsOnLinkPrefix    = true;
     onLinkPrefix.mPrefix            = prefix;
@@ -1218,7 +1256,7 @@
         }
         else
         {
-            otLogWarnBr("Discovered too many prefixes, ignore new on-link prefix %s", prefix.ToString().AsCString());
+            LogWarn("Discovered too many prefixes, ignore new on-link prefix %s", prefix.ToString().AsCString());
             ExitNow();
         }
     }
@@ -1271,7 +1309,7 @@
 
     if (!IsValidOmrPrefix(prefix))
     {
-        otLogInfoBr("Ignore invalid OMR prefix in RIO: %s", prefix.ToString().AsCString());
+        LogInfo("Ignore invalid OMR prefix in RIO: %s", prefix.ToString().AsCString());
         ExitNow();
     }
 
@@ -1291,8 +1329,8 @@
     VerifyOrExit(!mAdvertisedOmrPrefixes.Contains(prefix));
     VerifyOrExit(!NetworkDataContainsOmrPrefix(prefix));
 
-    otLogInfoBr("Discovered OMR prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(),
-                aRio.GetRouteLifetime(), mInfraIfIndex);
+    LogInfo("Discovered OMR prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(),
+            aRio.GetRouteLifetime(), mInfraIfIndex);
 
     if (aRio.GetRouteLifetime() == 0)
     {
@@ -1328,7 +1366,7 @@
         }
         else
         {
-            otLogWarnBr("Discovered too many prefixes, ignore new prefix %s", prefix.ToString().AsCString());
+            LogWarn("Discovered too many prefixes, ignore new prefix %s", prefix.ToString().AsCString());
             ExitNow();
         }
     }
@@ -1493,14 +1531,14 @@
     {
         if (mDiscoveredPrefixStaleTimer.IsRunning())
         {
-            otLogDebgBr("Prefix stale timer stopped");
+            LogDebg("Prefix stale timer stopped");
         }
         mDiscoveredPrefixStaleTimer.Stop();
     }
     else
     {
         mDiscoveredPrefixStaleTimer.FireAt(nextStaleTime);
-        otLogDebgBr("Prefix stale timer scheduled in %lu ms", nextStaleTime - now);
+        LogDebg("Prefix stale timer scheduled in %lu ms", nextStaleTime - now);
     }
 }
 
diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp
index 01325f8..0023e9e 100644
--- a/src/core/border_router/routing_manager.hpp
+++ b/src/core/border_router/routing_manager.hpp
@@ -185,6 +185,25 @@
      */
     Error HandleInfraIfStateChanged(uint32_t aInfraIfIndex, bool aIsRunning);
 
+    /**
+     * This method checks if the on-mesh prefix configuration is a valid OMR prefix.
+     *
+     * @param[in] aOnMeshPrefixConfig  The on-mesh prefix configuration to check.
+     *
+     * @returns  Whether the on-mesh prefix configuration is a valid OMR prefix.
+     *
+     */
+    static bool IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig);
+
+    /**
+     * This method checks if the OMR prefix is valid (i.e. GUA/ULA prefix with length being 64).
+     *
+     * @param[in]  aOmrPrefix  The OMR prefix to check.
+     * @returns    Whether the OMR prefix is valid.
+     *
+     */
+    static bool IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix);
+
 private:
     typedef NetworkData::RoutePreference RoutePreference;
 
@@ -194,10 +213,15 @@
     static constexpr uint8_t kMaxOmrPrefixNum = OPENTHREAD_CONFIG_IP6_SLAAC_NUM_ADDRESSES;
 
     // The maximum number of prefixes to discover on the infra link.
-    static constexpr uint8_t kMaxDiscoveredPrefixNum = 8;
+    static constexpr uint8_t kMaxDiscoveredPrefixNum = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES;
 
     static constexpr uint8_t kOmrPrefixLength    = OT_IP6_PREFIX_BITSIZE; // The length of an OMR prefix. In bits.
     static constexpr uint8_t kOnLinkPrefixLength = OT_IP6_PREFIX_BITSIZE; // The length of an On-link prefix. In bits.
+    static constexpr uint8_t kBrUlaPrefixLength  = 48;                    // The length of a BR ULA prefix. In bits.
+    static constexpr uint8_t kNat64PrefixLength  = 96;                    // The length of a NAT64 prefix. In bits.
+
+    static constexpr uint16_t kOmrPrefixSubnetId   = 1; // The subnet ID of an OMR prefix within a BR ULA prefix.
+    static constexpr uint16_t kNat64PrefixSubnetId = 2; // The subnet ID of a NAT64 prefix within a BR ULA prefix.
 
     // The maximum number of initial Router Advertisements.
     static constexpr uint32_t kMaxInitRtrAdvertisements = 3;
@@ -214,7 +238,9 @@
     static constexpr uint32_t kRtrSolicitationInterval     = 4;      // Interval between RSs. In sec.
     static constexpr uint32_t kMaxRtrSolicitationDelay     = 1;      // Max delay for initial solicitation. In sec.
     static constexpr uint32_t kRoutingPolicyEvaluationJitter = 1000; // Jitter for routing policy evaluation. In msec.
-    static constexpr uint32_t kRtrSolicitationRetryDelay     = 60;   // The delay before retrying failed RS tx. In Sec.
+    static constexpr uint32_t kRtrSolicitationRetryDelay =
+        kRtrSolicitationInterval;                             // The delay before retrying failed RS tx. In Sec.
+    static constexpr uint32_t kMinDelayBetweenRtrAdvs = 3000; // Min delay (msec) between consecutive RAs.
 
     // The STALE_RA_TIME in seconds. The Routing Manager will consider the prefixes
     // and learned RA parameters STALE when they are not refreshed in STALE_RA_TIME
@@ -289,14 +315,15 @@
     void  HandleNotifierEvents(Events aEvents);
     bool  IsInitialized(void) const { return mInfraIfIndex != 0; }
     bool  IsEnabled(void) const { return mIsEnabled; }
-    Error LoadOrGenerateRandomOmrPrefix(void);
+    Error LoadOrGenerateRandomBrUlaPrefix(void);
+    void  GenerateOmrPrefix(void);
     Error LoadOrGenerateRandomOnLinkPrefix(void);
 
     const Ip6::Prefix *EvaluateOnLinkPrefix(void);
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE
-    Error LoadOrGenerateRandomNat64Prefix(void);
-    void  EvaluateNat64Prefix(void);
+    void GenerateNat64Prefix(void);
+    void EvaluateNat64Prefix(void);
 #endif
 
     void  EvaluateRoutingPolicy(void);
@@ -305,6 +332,7 @@
     void  EvaluateOmrPrefix(OmrPrefixArray &aNewOmrPrefixes);
     Error PublishLocalOmrPrefix(void);
     void  UnpublishLocalOmrPrefix(void);
+    bool  IsOmrPrefixAddedToLocalNetworkData(void) const;
     Error AddExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64 = false);
     void  RemoveExternalRoute(const Ip6::Prefix &aPrefix);
     void  StartRouterSolicitationDelay(void);
@@ -337,8 +365,7 @@
     bool UpdateRouterAdvMessage(const RouterAdv::RouterAdvMessage *aRouterAdvMessage);
     void ResetDiscoveredPrefixStaleTimer(void);
 
-    static bool IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig);
-    static bool IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix);
+    static bool IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix);
     static bool IsValidOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio);
     static bool IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix);
 
@@ -357,8 +384,11 @@
     // messages will be sent.
     uint32_t mInfraIfIndex;
 
-    // The OMR prefix loaded from local persistent storage or randomly
-    // generated if non is found in persistent storage.
+    // The /48 BR ULA prefix loaded from local persistent storage or
+    // randomly generated if none is found in persistent storage.
+    Ip6::Prefix mBrUlaPrefix;
+
+    // The OMR prefix allocated from the /48 BR ULA prefix.
     Ip6::Prefix mLocalOmrPrefix;
 
     // The advertised OMR prefixes. For a stable Thread network without
@@ -380,8 +410,7 @@
     TimeMilli  mTimeAdvertisedOnLinkPrefix;
     TimerMilli mOnLinkPrefixDeprecateTimer;
 
-    // The NAT64 prefix loaded from local persistent storage or
-    // randomly generated if none is found in persistent storage.
+    // The NAT64 prefix allocated from the /48 BR ULA prefix.
     Ip6::Prefix mLocalNat64Prefix;
 
     // True if the local NAT64 prefix is advertised in Thread network.
@@ -403,7 +432,8 @@
     TimerMilli mDiscoveredPrefixInvalidTimer;
     TimerMilli mDiscoveredPrefixStaleTimer;
 
-    uint32_t mRouterAdvertisementCount;
+    uint32_t  mRouterAdvertisementCount;
+    TimeMilli mLastRouterAdvertisementSendTime;
 
 #if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE
     TimerMilli mVicariousRouterSolicitTimer;
diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp
index 37ceff9..f0b966d 100644
--- a/src/core/coap/coap.cpp
+++ b/src/core/coap/coap.cpp
@@ -28,12 +28,13 @@
 
 #include "coap.hpp"
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/random.hpp"
 #include "net/ip6.hpp"
 #include "net/udp6.hpp"
@@ -47,6 +48,8 @@
 namespace ot {
 namespace Coap {
 
+RegisterLogModule("Coap");
+
 CoapBase::CoapBase(Instance &aInstance, Sender aSender)
     : InstanceLocator(aInstance)
     , mMessageId(Random::NonCrypto::GetUint16())
@@ -76,18 +79,15 @@
 
 void CoapBase::ClearRequests(const Ip6::Address *aAddress)
 {
-    Message *nextMessage;
-
-    for (Message *message = mPendingRequests.GetHead(); message != nullptr; message = nextMessage)
+    for (Message &message : mPendingRequests)
     {
         Metadata metadata;
 
-        nextMessage = message->GetNextCoapMessage();
-        metadata.ReadFrom(*message);
+        metadata.ReadFrom(message);
 
         if ((aAddress == nullptr) || (metadata.mSourceAddress == *aAddress))
         {
-            FinalizeCoapTransaction(*message, metadata, nullptr, nullptr, kErrorAbort);
+            FinalizeCoapTransaction(message, metadata, nullptr, nullptr, kErrorAbort);
         }
     }
 }
@@ -139,6 +139,64 @@
     return message;
 }
 
+Message *CoapBase::NewPriorityConfirmablePostMessage(const char *aUriPath)
+{
+    return InitMessage(NewPriorityMessage(), kTypeConfirmable, aUriPath);
+}
+
+Message *CoapBase::NewConfirmablePostMessage(const char *aUriPath)
+{
+    return InitMessage(NewMessage(), kTypeConfirmable, aUriPath);
+}
+
+Message *CoapBase::NewPriorityNonConfirmablePostMessage(const char *aUriPath)
+{
+    return InitMessage(NewPriorityMessage(), kTypeNonConfirmable, aUriPath);
+}
+
+Message *CoapBase::NewNonConfirmablePostMessage(const char *aUriPath)
+{
+    return InitMessage(NewMessage(), kTypeNonConfirmable, aUriPath);
+}
+
+Message *CoapBase::NewPriorityResponseMessage(const Message &aRequest)
+{
+    return InitResponse(NewPriorityMessage(), aRequest);
+}
+
+Message *CoapBase::NewResponseMessage(const Message &aRequest)
+{
+    return InitResponse(NewMessage(), aRequest);
+}
+
+Message *CoapBase::InitMessage(Message *aMessage, Type aType, const char *aUriPath)
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(aMessage != nullptr);
+
+    SuccessOrExit(error = aMessage->Init(aType, kCodePost, aUriPath));
+    SuccessOrExit(error = aMessage->SetPayloadMarker());
+
+exit:
+    FreeAndNullMessageOnError(aMessage, error);
+    return aMessage;
+}
+
+Message *CoapBase::InitResponse(Message *aMessage, const Message &aResponse)
+{
+    Error error = kErrorNone;
+
+    VerifyOrExit(aMessage != nullptr);
+
+    SuccessOrExit(error = aMessage->SetDefaultResponseHeader(aResponse));
+    SuccessOrExit(error = aMessage->SetPayloadMarker());
+
+exit:
+    FreeAndNullMessageOnError(aMessage, error);
+    return aMessage;
+}
+
 Error CoapBase::Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
 {
     Error error;
@@ -418,19 +476,16 @@
     TimeMilli        now      = TimerMilli::GetNow();
     TimeMilli        nextTime = now.GetDistantFuture();
     Metadata         metadata;
-    Message *        nextMessage;
     Ip6::MessageInfo messageInfo;
 
-    for (Message *message = mPendingRequests.GetHead(); message != nullptr; message = nextMessage)
+    for (Message &message : mPendingRequests)
     {
-        nextMessage = message->GetNextCoapMessage();
-
-        metadata.ReadFrom(*message);
+        metadata.ReadFrom(message);
 
         if (now >= metadata.mNextTimerShot)
         {
 #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE
-            if (message->IsRequest() && metadata.mObserve && metadata.mAcknowledged)
+            if (message.IsRequest() && metadata.mObserve && metadata.mAcknowledged)
             {
                 // This is a RFC7641 subscription.  Do not time out.
                 continue;
@@ -440,7 +495,7 @@
             if (!metadata.mConfirmable || (metadata.mRetransmissionsRemaining == 0))
             {
                 // No expected response or acknowledgment.
-                FinalizeCoapTransaction(*message, metadata, nullptr, nullptr, kErrorResponseTimeout);
+                FinalizeCoapTransaction(message, metadata, nullptr, nullptr, kErrorResponseTimeout);
                 continue;
             }
 
@@ -448,7 +503,7 @@
             metadata.mRetransmissionsRemaining--;
             metadata.mRetransmissionTimeout *= 2;
             metadata.mNextTimerShot = now + metadata.mRetransmissionTimeout;
-            metadata.UpdateIn(*message);
+            metadata.UpdateIn(message);
 
             // Retransmit
             if (!metadata.mAcknowledged)
@@ -462,7 +517,7 @@
 #endif
                 messageInfo.SetMulticastLoop(metadata.mMulticastLoop);
 
-                SendCopy(*message, messageInfo);
+                SendCopy(message, messageInfo);
             }
         }
 
@@ -495,17 +550,15 @@
 Error CoapBase::AbortTransaction(ResponseHandler aHandler, void *aContext)
 {
     Error    error = kErrorNotFound;
-    Message *nextMessage;
     Metadata metadata;
 
-    for (Message *message = mPendingRequests.GetHead(); message != nullptr; message = nextMessage)
+    for (Message &message : mPendingRequests)
     {
-        nextMessage = message->GetNextCoapMessage();
-        metadata.ReadFrom(*message);
+        metadata.ReadFrom(message);
 
         if (metadata.mResponseHandler == aHandler && metadata.mResponseContext == aContext)
         {
-            FinalizeCoapTransaction(*message, metadata, nullptr, nullptr, kErrorAbort);
+            FinalizeCoapTransaction(message, metadata, nullptr, nullptr, kErrorAbort);
             error = kErrorNone;
         }
     }
@@ -676,8 +729,8 @@
 
     DequeueMessage(aRequest);
 
-    otLogInfoCoap("Send Block1 Nr. %d, Size: %d bytes, More Blocks Flag: %d", request->GetBlockWiseBlockNumber(),
-                  otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize()), request->IsMoreBlocksFlagSet());
+    LogInfo("Send Block1 Nr. %d, Size: %d bytes, More Blocks Flag: %d", request->GetBlockWiseBlockNumber(),
+            otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize()), request->IsMoreBlocksFlagSet());
 
     SuccessOrExit(error = SendMessage(*request, aMessageInfo, TxParameters::GetDefault(),
                                       aCoapMetadata.mResponseHandler, aCoapMetadata.mResponseContext,
@@ -718,8 +771,8 @@
                                                     bufLen, aMessage.IsMoreBlocksFlagSet(), aTotalLength));
 
     // CoAP Block-Wise Transfer continues
-    otLogInfoCoap("Received Block2 Nr. %d , Size: %d bytes, More Blocks Flag: %d", aMessage.GetBlockWiseBlockNumber(),
-                  otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()), aMessage.IsMoreBlocksFlagSet());
+    LogInfo("Received Block2 Nr. %d , Size: %d bytes, More Blocks Flag: %d", aMessage.GetBlockWiseBlockNumber(),
+            otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()), aMessage.IsMoreBlocksFlagSet());
 
     // Conclude block-wise transfer if last block has been received
     if (!aMessage.IsMoreBlocksFlagSet())
@@ -738,8 +791,8 @@
         DequeueMessage(aRequest);
     }
 
-    otLogInfoCoap("Request Block2 Nr. %d, Size: %d bytes", request->GetBlockWiseBlockNumber(),
-                  otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize()));
+    LogInfo("Request Block2 Nr. %d, Size: %d bytes", request->GetBlockWiseBlockNumber(),
+            otCoapBlockSizeFromExponent(request->GetBlockWiseBlockSize()));
 
     SuccessOrExit(error =
                       SendMessage(*request, aMessageInfo, TxParameters::GetDefault(), aCoapMetadata.mResponseHandler,
@@ -789,8 +842,8 @@
 
         SuccessOrExit(error = CacheLastBlockResponse(response));
 
-        otLogInfoCoap("Acknowledge Block1 Nr. %d, Size: %d bytes", response->GetBlockWiseBlockNumber(),
-                      otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()));
+        LogInfo("Acknowledge Block1 Nr. %d, Size: %d bytes", response->GetBlockWiseBlockNumber(),
+                otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()));
 
         SuccessOrExit(error = SendMessage(*response, aMessageInfo));
 
@@ -826,8 +879,8 @@
 
     SuccessOrExit(error = aMessage.ReadBlockOptionValues(kOptionBlock2));
 
-    otLogInfoCoap("Request for Block2 Nr. %d, Size: %d bytes received", aMessage.GetBlockWiseBlockNumber(),
-                  otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()));
+    LogInfo("Request for Block2 Nr. %d, Size: %d bytes received", aMessage.GetBlockWiseBlockNumber(),
+            otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize()));
 
     if (aMessage.GetBlockWiseBlockNumber() == 0)
     {
@@ -926,8 +979,8 @@
         FreeLastBlockResponse();
     }
 
-    otLogInfoCoap("Send Block2 Nr. %d, Size: %d bytes, More Blocks Flag %d", response->GetBlockWiseBlockNumber(),
-                  otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()), response->IsMoreBlocksFlagSet());
+    LogInfo("Send Block2 Nr. %d, Size: %d bytes, More Blocks Flag %d", response->GetBlockWiseBlockNumber(),
+            otCoapBlockSizeFromExponent(response->GetBlockWiseBlockSize()), response->IsMoreBlocksFlagSet());
 
     SuccessOrExit(error = SendMessage(*response, aMessageInfo));
 
@@ -953,7 +1006,7 @@
 
     if (error != kErrorNone)
     {
-        otLogWarnCoap("Failed to send copy: %s", ErrorToString(error));
+        LogWarn("Failed to send copy: %s", ErrorToString(error));
         FreeMessage(messageCopy);
     }
 }
@@ -962,11 +1015,11 @@
                                       const Ip6::MessageInfo &aMessageInfo,
                                       Metadata &              aMetadata)
 {
-    Message *message;
+    Message *request = nullptr;
 
-    for (message = mPendingRequests.GetHead(); message != nullptr; message = message->GetNextCoapMessage())
+    for (Message &message : mPendingRequests)
     {
-        aMetadata.ReadFrom(*message);
+        aMetadata.ReadFrom(message);
 
         if (((aMetadata.mDestinationAddress == aMessageInfo.GetPeerAddr()) ||
              aMetadata.mDestinationAddress.IsMulticast() ||
@@ -977,8 +1030,9 @@
             {
             case kTypeReset:
             case kTypeAck:
-                if (aResponse.GetMessageId() == message->GetMessageId())
+                if (aResponse.GetMessageId() == message.GetMessageId())
                 {
+                    request = &message;
                     ExitNow();
                 }
 
@@ -986,8 +1040,9 @@
 
             case kTypeConfirmable:
             case kTypeNonConfirmable:
-                if (aResponse.IsTokenEqual(*message))
+                if (aResponse.IsTokenEqual(message))
                 {
+                    request = &message;
                     ExitNow();
                 }
 
@@ -997,7 +1052,7 @@
     }
 
 exit:
-    return message;
+    return request;
 }
 
 void CoapBase::Receive(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
@@ -1006,7 +1061,7 @@
 
     if (message.ParseHeader() != kErrorNone)
     {
-        otLogDebgCoap("Failed to parse CoAP header");
+        LogDebg("Failed to parse CoAP header");
 
         if (!aMessageInfo.GetSockAddr().IsMulticast() && message.IsConfirmable())
         {
@@ -1289,7 +1344,7 @@
                 *curUriPath++ = '/';
             }
 
-            VerifyOrExit(curUriPath + iterator.GetOption()->GetLength() < OT_ARRAY_END(uriPath), error = kErrorParse);
+            VerifyOrExit(curUriPath + iterator.GetOption()->GetLength() < GetArrayEnd(uriPath), error = kErrorParse);
 
             IgnoreError(iterator.ReadOptionValue(curUriPath));
             curUriPath += iterator.GetOption()->GetLength();
@@ -1398,7 +1453,7 @@
 
     if (error != kErrorNone)
     {
-        otLogInfoCoap("Failed to process request: %s", ErrorToString(error));
+        LogInfo("Failed to process request: %s", ErrorToString(error));
 
         if (error == kErrorNotFound && !aMessageInfo.GetSockAddr().IsMulticast())
         {
@@ -1446,25 +1501,26 @@
 
 const Message *ResponsesQueue::FindMatchedResponse(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo) const
 {
-    Message *message;
+    const Message *response = nullptr;
 
-    for (message = mQueue.GetHead(); message != nullptr; message = message->GetNextCoapMessage())
+    for (const Message &message : mQueue)
     {
-        if (message->GetMessageId() == aRequest.GetMessageId())
+        if (message.GetMessageId() == aRequest.GetMessageId())
         {
             ResponseMetadata metadata;
 
-            metadata.ReadFrom(*message);
+            metadata.ReadFrom(message);
 
             if ((metadata.mMessageInfo.GetPeerPort() == aMessageInfo.GetPeerPort()) &&
                 (metadata.mMessageInfo.GetPeerAddr() == aMessageInfo.GetPeerAddr()))
             {
+                response = &message;
                 break;
             }
         }
     }
 
-    return message;
+    return response;
 }
 
 void ResponsesQueue::EnqueueResponse(Message &               aMessage,
@@ -1503,15 +1559,15 @@
     // `kMaxCachedResponses` remove the one with earliest dequeue
     // time.
 
-    for (Message *message = mQueue.GetHead(); message != nullptr; message = message->GetNextCoapMessage())
+    for (Message &message : mQueue)
     {
         ResponseMetadata metadata;
 
-        metadata.ReadFrom(*message);
+        metadata.ReadFrom(message);
 
         if ((earliestMsg == nullptr) || (metadata.mDequeueTime < earliestDequeueTime))
         {
-            earliestMsg         = message;
+            earliestMsg         = &message;
             earliestDequeueTime = metadata.mDequeueTime;
         }
 
@@ -1543,19 +1599,16 @@
 {
     TimeMilli now             = TimerMilli::GetNow();
     TimeMilli nextDequeueTime = now.GetDistantFuture();
-    Message * nextMessage;
 
-    for (Message *message = mQueue.GetHead(); message != nullptr; message = nextMessage)
+    for (Message &message : mQueue)
     {
         ResponseMetadata metadata;
 
-        nextMessage = message->GetNextCoapMessage();
-
-        metadata.ReadFrom(*message);
+        metadata.ReadFrom(message);
 
         if (now >= metadata.mDequeueTime)
         {
-            DequeueResponse(*message);
+            DequeueResponse(message);
             continue;
         }
 
diff --git a/src/core/coap/coap.hpp b/src/core/coap/coap.hpp
index d2bd0a8..aea240e 100644
--- a/src/core/coap/coap.hpp
+++ b/src/core/coap/coap.hpp
@@ -435,7 +435,7 @@
     void SetDefaultHandler(RequestHandler aHandler, void *aContext);
 
     /**
-     * This method creates a new message with a CoAP header.
+     * This method allocates a new message with a CoAP header.
      *
      * @param[in]  aSettings  The message settings.
      *
@@ -445,7 +445,7 @@
     Message *NewMessage(const Message::Settings &aSettings = Message::Settings::GetDefault());
 
     /**
-     * This method creates a new message with a CoAP header that has Network Control priority level.
+     * This method allocates a new message with a CoAP header that has Network Control priority level.
      *
      * @returns A pointer to the message or `nullptr` if failed to allocate message.
      *
@@ -455,6 +455,95 @@
         return NewMessage(Message::Settings(Message::kWithLinkSecurity, Message::kPriorityNet));
     }
 
+    /**
+     * This method allocates and initializes a new CoAP Confirmable Post message with Network Control priority level.
+     *
+     * The CoAP header is initialized as `kTypeConfirmable` and `kCodePost` with a given URI path and a randomly
+     * generated token (of default length). This method also sets the payload marker (`SetPayloadMarker()` on message.
+     * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and
+     * remove the payload marker when there is no payload.
+     *
+     * @param[in] aUriPath   The URI path string.
+     *
+     * @returns A pointer to the message or `nullptr` if failed to allocate message.
+     *
+     */
+    Message *NewPriorityConfirmablePostMessage(const char *aUriPath);
+
+    /**
+     * This method allocates and initializes a new CoAP Confirmable Post message with normal priority level.
+     *
+     * The CoAP header is initialized as `kTypeConfirmable` and `kCodePost` with a given URI path and a randomly
+     * generated token (of default length). This method also sets the payload marker (calling `SetPayloadMarker()`).
+     * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and
+     * remove the payload marker when there is no payload.
+     *
+     * @param[in] aUriPath   The URI path string.
+     *
+     * @returns A pointer to the message or `nullptr` if failed to allocate message.
+     *
+     */
+    Message *NewConfirmablePostMessage(const char *aUriPath);
+
+    /**
+     * This method allocates and initializes a new CoAP Non-confirmable Post message with Network Control priority
+     * level.
+     *
+     * The CoAP header is initialized as `kTypeNonConfirmable` and `kCodePost` with a given URI path and a randomly
+     * generated token (of default length). This method also sets the payload marker (calling `SetPayloadMarker()`).
+     * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and
+     * remove the payload marker when there is no payload.
+     *
+     * @param[in] aUriPath   The URI path string.
+     *
+     * @returns A pointer to the message or `nullptr` if failed to allocate message.
+     *
+     */
+    Message *NewPriorityNonConfirmablePostMessage(const char *aUriPath);
+
+    /**
+     * This method allocates and initializes a new CoAP Non-confirmable Post message with normal priority level.
+     *
+     * The CoAP header is initialized as `kTypeNonConfirmable` and `kCodePost` with a given URI path and a randomly
+     * generated token (of default length). This method also sets the payload marker (calling `SetPayloadMarker()`).
+     * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and
+     * remove the payload marker when there is no payload.
+     *
+     * @param[in] aUriPath   The URI path string.
+     *
+     * @returns A pointer to the message or `nullptr` if failed to allocate message.
+     *
+     */
+    Message *NewNonConfirmablePostMessage(const char *aUriPath);
+
+    /**
+     * This method allocates and initializes a new CoAP response message with Network Control priority level for a
+     * given request message.
+     *
+     * The CoAP header is initialized as `kTypeAck` with `kCodeChanged`. The token and message ID is copied from
+     * @p aRequest. This method also sets the payload marker (calling `SetPayloadMarker()`). Even if message has
+     * no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and remove the payload
+     * marker when there is no payload.
+     *
+     * @returns A pointer to the message or `nullptr` if failed to allocate message.
+     *
+     */
+    Message *NewPriorityResponseMessage(const Message &aRequest);
+
+    /**
+     * This method allocates and initializes a new CoAP response message with regular priority level for a given
+     * request message.
+     *
+     * The CoAP header is initialized as `kTypeAck` with `kCodeChanged`. The token and message ID is copied from
+     * @p aRequest. This method also sets the payload marker (calling `SetPayloadMarker()`). Even if message has
+     * no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and remove the payload
+     * marker when there is no payload.
+     *
+     * @returns A pointer to the message or `nullptr` if failed to allocate message.
+     *
+     */
+    Message *NewResponseMessage(const Message &aRequest);
+
 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
     /**
      * This method sends a CoAP message block-wise with custom transmission parameters.
@@ -718,6 +807,9 @@
 #endif
     };
 
+    Message *InitMessage(Message *aMessage, Type aType, const char *aUriPath);
+    Message *InitResponse(Message *aMessage, const Message &aResponse);
+
     static void HandleRetransmissionTimer(Timer &aTimer);
     void        HandleRetransmissionTimer(void);
 
diff --git a/src/core/coap/coap_message.cpp b/src/core/coap/coap_message.cpp
index 5d02662..bd1c152 100644
--- a/src/core/coap/coap_message.cpp
+++ b/src/core/coap/coap_message.cpp
@@ -34,6 +34,7 @@
 #include "coap_message.hpp"
 
 #include "coap/coap.hpp"
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 #include "common/debug.hpp"
 #include "common/encoding.hpp"
@@ -78,26 +79,6 @@
     return error;
 }
 
-void Message::InitAsConfirmablePost(void)
-{
-    Init(kTypeConfirmable, kCodePost);
-}
-
-void Message::InitAsNonConfirmablePost(void)
-{
-    Init(kTypeNonConfirmable, kCodePost);
-}
-
-Error Message::InitAsConfirmablePost(const char *aUriPath)
-{
-    return Init(kTypeConfirmable, kCodePost, aUriPath);
-}
-
-Error Message::InitAsNonConfirmablePost(const char *aUriPath)
-{
-    return Init(kTypeNonConfirmable, kCodePost, aUriPath);
-}
-
 Error Message::InitAsPost(const Ip6::Address &aDestination, const char *aUriPath)
 {
     return Init(aDestination.IsMulticast() ? kTypeNonConfirmable : kTypeConfirmable, kCodePost, aUriPath);
@@ -259,7 +240,7 @@
             *curUriPath++ = '/';
         }
 
-        VerifyOrExit(curUriPath + optionLength < OT_ARRAY_END(aUriPath), error = kErrorParse);
+        VerifyOrExit(curUriPath + optionLength < GetArrayEnd(aUriPath), error = kErrorParse);
 
         IgnoreError(iterator.ReadOptionValue(curUriPath));
         curUriPath += optionLength;
@@ -475,6 +456,16 @@
 }
 #endif // OPENTHREAD_CONFIG_COAP_API_ENABLE
 
+Message::Iterator MessageQueue::begin(void)
+{
+    return Message::Iterator(GetHead());
+}
+
+Message::ConstIterator MessageQueue::begin(void) const
+{
+    return Message::ConstIterator(GetHead());
+}
+
 Error Option::Iterator::Init(const Message &aMessage)
 {
     Error    error  = kErrorParse;
diff --git a/src/core/coap/coap_message.hpp b/src/core/coap/coap_message.hpp
index cf56cb0..1f4a86b 100644
--- a/src/core/coap/coap_message.hpp
+++ b/src/core/coap/coap_message.hpp
@@ -166,6 +166,7 @@
 class Message : public ot::Message
 {
     friend class Option;
+    friend class MessageQueue;
 
 public:
     static constexpr uint8_t kDefaultTokenLength = OT_COAP_DEFAULT_TOKEN_LENGTH; ///< Default token length.
@@ -203,18 +204,6 @@
     void Init(Type aType, Code aCode);
 
     /**
-     * This method initializes the CoAP header as `kTypeConfirmable` and `kCodePost`.
-     *
-     */
-    void InitAsConfirmablePost(void);
-
-    /**
-     * This method initializes the CoAP header as `kTypeNonConfirmable` and `kCodePost`.
-     *
-     */
-    void InitAsNonConfirmablePost(void);
-
-    /**
      * This method initializes the CoAP header with specific Type and Code.
      *
      * @param[in]  aType              The Type value.
@@ -228,28 +217,6 @@
     Error Init(Type aType, Code aCode, const char *aUriPath);
 
     /**
-     * This method initializes the CoAP header as `kTypeConfirmable` and `kCodePost` with a given URI Path.
-     *
-     * @param[in]  aUriPath           A pointer to a null-terminated string.
-     *
-     * @retval kErrorNone         Successfully appended the option.
-     * @retval kErrorNoBufs       The option length exceeds the buffer size.
-     *
-     */
-    Error InitAsConfirmablePost(const char *aUriPath);
-
-    /**
-     * This method initializes the CoAP header as `kTypeNonConfirmable` and `kCodePost` with a given URI Path.
-     *
-     * @param[in]  aUriPath           A pointer to a null-terminated string.
-     *
-     * @retval kErrorNone         Successfully appended the option.
-     * @retval kErrorNoBufs       The option length exceeds the buffer size.
-     *
-     */
-    Error InitAsNonConfirmablePost(const char *aUriPath);
-
-    /**
      * This method initializes the CoAP header as `kCodePost` with a given URI Path with its type determined from a
      * given destination IPv6 address.
      *
@@ -956,6 +923,27 @@
 #endif
     };
 
+    class ConstIterator : public ot::Message::ConstIterator
+    {
+    public:
+        using ot::Message::ConstIterator::ConstIterator;
+
+        const Message &operator*(void) { return static_cast<const Message &>(ot::Message::ConstIterator::operator*()); }
+        const Message *operator->(void)
+        {
+            return static_cast<const Message *>(ot::Message::ConstIterator::operator->());
+        }
+    };
+
+    class Iterator : public ot::Message::Iterator
+    {
+    public:
+        using ot::Message::Iterator::Iterator;
+
+        Message &operator*(void) { return static_cast<Message &>(ot::Message::Iterator::operator*()); }
+        Message *operator->(void) { return static_cast<Message *>(ot::Message::Iterator::operator->()); }
+    };
+
     static_assert(sizeof(HelpData) <= sizeof(Ip6::Header) + sizeof(Ip6::HopByHopHeader) + sizeof(Ip6::OptionMpl) +
                                           sizeof(Ip6::Udp::Header),
                   "HelpData size exceeds the size of the reserved region in the message");
@@ -1000,7 +988,15 @@
      * @returns A pointer to the first message.
      *
      */
-    Message *GetHead(void) const { return static_cast<Message *>(ot::MessageQueue::GetHead()); }
+    Message *GetHead(void) { return static_cast<Message *>(ot::MessageQueue::GetHead()); }
+
+    /**
+     * This method returns a pointer to the first message.
+     *
+     * @returns A pointer to the first message.
+     *
+     */
+    const Message *GetHead(void) const { return static_cast<const Message *>(ot::MessageQueue::GetHead()); }
 
     /**
      * This method adds a message to the end of the queue.
@@ -1034,6 +1030,17 @@
      *
      */
     void DequeueAndFree(Message &aMessage) { ot::MessageQueue::DequeueAndFree(aMessage); }
+
+    // The following methods are intended to support range-based `for`
+    // loop iteration over the queue entries and should not be used
+    // directly. The range-based `for` works correctly even if the
+    // current entry is removed from the queue during iteration.
+
+    Message::Iterator begin(void);
+    Message::Iterator end(void) { return Message::Iterator(); }
+
+    Message::ConstIterator begin(void) const;
+    Message::ConstIterator end(void) const { return Message::ConstIterator(); }
 };
 
 /**
diff --git a/src/core/coap/coap_secure.cpp b/src/core/coap/coap_secure.cpp
index b4b5197..d158731 100644
--- a/src/core/coap/coap_secure.cpp
+++ b/src/core/coap/coap_secure.cpp
@@ -32,7 +32,7 @@
 
 #include "common/instance.hpp"
 #include "common/locator_getters.hpp"
-#include "common/logging.hpp"
+#include "common/log.hpp"
 #include "common/new.hpp"
 #include "meshcop/dtls.hpp"
 #include "thread/thread_netif.hpp"
@@ -45,6 +45,8 @@
 namespace ot {
 namespace Coap {
 
+RegisterLogModule("CoapSecure");
+
 CoapSecure::CoapSecure(Instance &aInstance, bool aLayerTwoSecurity)
     : CoapBase(aInstance, &CoapSecure::Send)
     , mDtls(aInstance, aLayerTwoSecurity)
@@ -221,12 +223,12 @@
 exit:
     if (error != kErrorNone)
     {
-        otLogNoteMeshCoP("CoapSecure Transmit: %s", ErrorToString(error));
+        LogNote("Transmit: %s", ErrorToString(error));
         message->Free();
     }
     else
     {
-        otLogDebgMeshCoP("CoapSecure Transmit: %s", ErrorToString(error));
+        LogDebg("Transmit: %s", ErrorToString(error));
     }
 }
 
diff --git a/src/core/coap/coap_secure.hpp b/src/core/coap/coap_secure.hpp
index cd01ada..be8caf6 100644
--- a/src/core/coap/coap_secure.hpp
+++ b/src/core/coap/coap_secure.hpp
@@ -209,8 +209,8 @@
      *
      * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS.
      *
-     * @param[in]  aX509Certificate   A pointer to the PEM formatted X509 PEM certificate.
-     * @param[in]  aX509CertLength    The length of certificate.
+     * @param[in]  aX509Cert          A pointer to the PEM formatted X509 PEM certificate.
+     * @param[in]  aX509Length        The length of certificate.
      * @param[in]  aPrivateKey        A pointer to the PEM formatted private key.
      * @param[in]  aPrivateKeyLength  The length of the private key.
      *
diff --git a/src/core/common/array.hpp b/src/core/common/array.hpp
index 9da3e77..ded006f 100644
--- a/src/core/common/array.hpp
+++ b/src/core/common/array.hpp
@@ -45,6 +45,61 @@
 namespace ot {
 
 /**
+ * This function returns the length of a given array (number of elements in the array).
+ *
+ * This template function is `constexpr`. The template arguments are expected to be deduced by the compiler allowing
+ * callers to simply use `GetArrayLength(aArray)`.
+ *
+ * @tparam  Type          The array element type.
+ * @tparam  kArrayLength  The array length.
+ *
+ * @returns The array length (number of elements in the array).
+ *
+ */
+template <typename Type, uint16_t kArrayLength> constexpr inline uint16_t GetArrayLength(const Type (&)[kArrayLength])
+{
+    return kArrayLength;
+}
+
+/**
+ * This function returns a pointer to end of a given array (pointing to the past-the-end element).
+ *
+ * Note that the past-the-end element is a theoretical element that would follow the last element in the array. It does
+ * not point to an actual element in array, and thus should not be dereferenced.
+ *
+ * @tparam  Type          The array element type.
+ * @tparam  kArrayLength  The array length.
+ *
+ * @param[in] aArray   A reference to the array.
+ *
+ * @returns Pointer to the past-the-end element.
+ *
+ */
+template <typename Type, uint16_t kArrayLength> inline Type *GetArrayEnd(Type (&aArray)[kArrayLength])
+{
+    return &aArray[kArrayLength];
+}
+
+/**
+ * This function returns a pointer to end of a given array (pointing to the past-the-end element).
+ *
+ * Note that the past-the-end element is a theoretical element that would follow the last element in the array. It does
+ * not point to an actual element in array, and thus should not be dereferenced.
+ *
+ * @tparam  Type          The array element type.
+ * @tparam  kArrayLength  The array length.
+ *
+ * @param[in] aArray   A reference to the array.
+ *
+ * @returns Pointer to the past-the-end element.
+ *
+ */
+template <typename Type, uint16_t kArrayLength> inline const Type *GetArrayEnd(const Type (&aArray)[kArrayLength])
+{
+    return &aArray[kArrayLength];
+}
+
+/**
  * This template class represents an array of elements with a fixed max size.
  *
  * @tparam Type        The array element type.
diff --git a/src/core/common/error.cpp b/src/core/common/error.cpp
index 5d4a92c..ec15c4a 100644
--- a/src/core/common/error.cpp
+++ b/src/core/common/error.cpp
@@ -33,6 +33,7 @@
 
 #include "error.hpp"
 
+#include "common/array.hpp"
 #include "common/code_utils.hpp"
 
 namespace ot {
@@ -80,7 +81,7 @@
         "Rejected",                   // (37) kErrorRejected
     };
 
-    return aError < OT_ARRAY_LENGTH(kErrorStrings) ? kErrorStrings[aError] : "UnknownErrorType";
+    return aError < GetArrayLength(kErrorStrings) ? kErrorStrings[aError] : "UnknownErrorType";
 }
 
 } // namespace ot
diff --git a/src/core/common/heap_array.hpp b/src/core/common/heap_array.hpp
new file mode 100644
index 0000000..353462d
--- /dev/null
+++ b/src/core/common/heap_array.hpp
@@ -0,0 +1,549 @@
+/*
+ *  Copyright (c) 2022, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file includes definitions for `Heap::Array` (a heap allocated array of flexible length).
+ */
+
+#ifndef HEAP_ARRAY_HPP_
+#define HEAP_ARRAY_HPP_
+
+#include "openthread-core-config.h"
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "common/array.hpp"
+#include "common/code_utils.hpp"
+#include "common/error.hpp"
+#include "common/heap.hpp"
+//#include "common/new.hpp"
+
+namespace ot {
+namespace Heap {
+
+/**
+ * This class represents a heap allocated array.
+ *
+ * The buffer to store the elements is allocated from heap and is managed by the `Heap::Array` class itself. The `Array`
+ * implementation will automatically grow the buffer when new entries are added. The `Heap::Array` destructor will
+ * always free the allocated buffer.
+ *
+ * The `Type` class MUST provide a move constructor `Type(Type &&aOther)` (or a copy constructor if no move constructor
+ * is provided). This constructor is used to move existing elements when array buffer is grown (new buffer is
+ * allocated) to make room for new elements.
+ *
+ * @tparam  Type                  The array element type.
+ * @tparam  kCapacityIncrements   Number of elements to allocate at a time when updating the array buffer.
+ *
+ */
+template <typename Type, uint16_t kCapacityIncrements = 2> class Array
+{
+public:
+    using IndexType = uint16_t;
+
+    /**
+     * This constructor initializes the `Array` as empty.
+     *
+     */
+    Array(void)
+        : mArray(nullptr)
+        , mLength(0)
+        , mCapacity(0)
+    {
+    }
+
+    /**
+     * This is the destructor for `Array` object.
+     *
+     */
+    ~Array(void) { Free(); }
+
+    /**
+     * This method frees any buffer allocated by the `Array`.
+     *
+     * The `Array` destructor will automatically call `Free()`. This method allows caller to free buffer explicitly.
+     *
+     */
+    void Free(void)
+    {
+        Clear();
+        Heap::Free(mArray);
+        mArray    = nullptr;
+        mCapacity = 0;
+    }
+
+    /**
+     * This method clears the array.
+     *
+     * Note that `Clear()` method (unlike `Free()`) does not free the allocated buffer and therefore does not change
+     * the current capacity of the array.
+     *
+     * This method invokes `Type` destructor on all cleared existing elements of array.
+     *
+     */
+    void Clear(void)
+    {
+        for (Type &entry : *this)
+        {
+            entry.~Type();
+        }
+
+        mLength = 0;
+    }
+
+    /**
+     * This method returns the current array length (number of elements in the array).
+     *
+     * @returns The array length.
+     *
+     */
+    IndexType GetLength(void) const { return mLength; }
+
+    /**
+     * This method returns a raw pointer to the array buffer.
+     *
+     * The returned raw pointer is valid only while the `Array` remains unchanged.
+     *
+     * @returns A pointer to the array buffer or `nullptr` if the array is empty.
+     *
+     */
+    const Type *AsCArray(void) const { return (mLength != 0) ? mArray : nullptr; }
+
+    /**
+     * This method returns the current capacity of array (number of elements that can fit in current allocated buffer).
+     *
+     * The allocated buffer and array capacity are automatically increased (by the `Array` itself) when new elements
+     * are added to array. Removing elements does not change the buffer and the capacity. A desired capacity can be
+     * reserved using `ReserveCapacity()` method.
+     *
+     * @returns The current capacity of the array.
+     *
+     */
+    IndexType GetCapacity(void) const { return mCapacity; }
+
+    /**
+     * This method allocates buffer to reserve a given capacity for array.
+     *
+     * If the requested @p aCapacity is smaller than the current length of the array, capacity remains unchanged.
+     *
+     * @param[in] aCapacity   The target capacity for the array.
+     *
+     * @retval kErrorNone     Array was successfully updated to support @p aCapacity.
+     * @retval kErrorNoBufs   Could not allocate buffer.
+     *
+     */
+    Error ReserveCapacity(IndexType aCapacity) { return Allocate(aCapacity); }
+
+    /**
+     * This method sets the array by taking the buffer from another given array (using move semantics).
+     *
+     * @param[in] aOther    The other `Heap::Array` to take from (rvalue reference).
+     *
+     */
+    void TakeFrom(Array &&aOther)
+    {
+        Free();
+        mArray           = aOther.mArray;
+        mLength          = aOther.mLength;
+        mCapacity        = aOther.mCapacity;
+        aOther.mArray    = nullptr;
+        aOther.mLength   = 0;
+        aOther.mCapacity = 0;
+    }
+
+    /**
+     * This method overloads the `[]` operator to get the element at a given index.
+     *
+     * This method does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
+     *
+     * @param[in] aIndex  The index to get.
+     *
+     * @returns A reference to the element in array at @p aIndex.
+     *
+     */
+    Type &operator[](IndexType aIndex) { return mArray[aIndex]; }
+
+    /**
+     * This method overloads the `[]` operator to get the element at a given index.
+     *
+     * This method does not perform index bounds checking. Behavior is undefined if @p aIndex is not valid.
+     *
+     * @param[in] aIndex  The index to get.
+     *
+     * @returns A reference to the element in array at @p aIndex.
+     *
+     */
+    const Type &operator[](IndexType aIndex) const { return mArray[aIndex]; }
+
+    /**
+     * This method gets a pointer to the element at a given index.
+     *
+     * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length. The returned
+     * pointer is valid only while the `Array` remains unchanged.
+     *
+     * @param[in] aIndex  The index to get.
+     *
+     * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid.
+     *
+     */
+    Type *At(IndexType aIndex) { return (aIndex < mLength) ? &mArray[aIndex] : nullptr; }
+
+    /**
+     * This method gets a pointer to the element at a given index.
+     *
+     * Unlike `operator[]`, this method checks @p aIndex to be valid and within the current length. The returned
+     * pointer is valid only while the `Array` remains unchanged.
+     *
+     * @param[in] aIndex  The index to get.
+     *
+     * @returns A pointer to element in array at @p aIndex or `nullptr` if @p aIndex is not valid.
+     *
+     */
+    const Type *At(IndexType aIndex) const { return (aIndex < mLength) ? &mArray[aIndex] : nullptr; }
+
+    /**
+     * This method gets a pointer to the element at the front of the array (first element).
+     *
+     * The returned pointer is valid only while the `Array` remains unchanged.
+     *
+     * @returns A pointer to the front element or `nullptr` if array is empty.
+     *
+     */
+    Type *Front(void) { return At(0); }
+
+    /**
+     * This method gets a pointer to the element at the front of the array (first element).
+     *
+     * The returned pointer is valid only while the `Array` remains unchanged.
+     *
+     * @returns A pointer to the front element or `nullptr` if array is empty.
+     *
+     */
+    const Type *Front(void) const { return At(0); }
+
+    /**
+     * This method gets a pointer to the element at the back of the array (last element).
+     *
+     * The returned pointer is valid only while the `Array` remains unchanged.
+     *
+     * @returns A pointer to the back element or `nullptr` if array is empty.
+     *
+     */
+    Type *Back(void) { return (mLength > 0) ? &mArray[mLength - 1] : nullptr; }
+
+    /**
+     * This method gets a pointer to the element at the back of the array (last element).
+     *
+     * The returned pointer is valid only while the `Array` remains unchanged.
+     *
+     * @returns A pointer to the back element or `nullptr` if array is empty.
+     *
+     */
+    const Type *Back(void) const { return (mLength > 0) ? &mArray[mLength - 1] : nullptr; }
+
+    /**
+     * This method appends a new entry to the end of the array.
+     *
+     * This method requires the `Type` to provide a copy constructor of format `Type(const Type &aOther)` to init the
+     * new element in the array from @p aEntry.
+     *
+     * @param[in] aEntry     The new entry to push back.
+     *
+     * @retval kErrorNone    Successfully pushed back @p aEntry to the end of the array.
+     * @retval kErrorNoBufs  Could not allocate buffer to grow the array.
+     *
+     */
+    Error PushBack(const Type &aEntry)
+    {
+        Error error = kErrorNone;
+
+        if (mLength == mCapacity)
+        {
+            SuccessOrExit(error = Allocate(mCapacity + kCapacityIncrements));
+        }
+
+        new (&mArray[mLength++]) Type(aEntry);
+
+    exit:
+        return error;
+    }
+
+    /**
+     * This method appends a new entry to the end of the array.
+     *
+     * This method requires the `Type` to provide a copy constructor of format `Type(Type &&aOther)` to init the
+     * new element in the array from @p aEntry.
+     *
+     * @param[in] aEntry     The new entry to push back (an rvalue reference)
+     *
+     * @retval kErrorNone    Successfully pushed back @p aEntry to the end of the array.
+     * @retval kErrorNoBufs  Could not allocate buffer to grow the array.
+     *
+     */
+    Error PushBack(Type &&aEntry)
+    {
+        Error error = kErrorNone;
+
+        if (mLength == mCapacity)
+        {
+            SuccessOrExit(error = Allocate(mCapacity + kCapacityIncrements));
+        }
+
+        new (&mArray[mLength++]) Type(static_cast<Type &&>(aEntry));
+
+    exit:
+        return error;
+    }
+
+    /**
+     * This method appends a new entry to the end of the array.
+     *
+     * On success, this method returns a pointer to the newly appended element in the array for the caller to
+     * initialize and use. This method uses the `Type(void)` default constructor on the newly appended element (if not
+     * `nullptr`).
+     *
+     * The returned pointer is valid only while the `Array` remains unchanged.
+     *
+     * @return A pointer to the newly appended element or `nullptr` if could not allocate buffer to grow the array
+     *
+     */
+    Type *PushBack(void)
+    {
+        Type *newEntry = nullptr;
+
+        if (mLength == mCapacity)
+        {
+            SuccessOrExit(Allocate(mCapacity + kCapacityIncrements));
+        }
+
+        newEntry = new (&mArray[mLength++]) Type();
+
+    exit:
+        return newEntry;
+    }
+
+    /**
+     * This method removes the last element in the array.
+     *
+     * This method will invoke the `Type` destructor on the removed element.
+     *
+     */
+    void PopBack(void)
+    {
+        if (mLength > 0)
+        {
+            mArray[mLength - 1].~Type();
+            mLength--;
+        }
+    }
+
+    /**
+     * This method returns the index of an element in the array.
+     *
+     * The @p aElement MUST be from the array, otherwise the behavior of this method is undefined.
+     *
+     * @param[in] aElement  A reference to an element in the array.
+     *
+     * @returns The index of @p aElement in the array.
+     *
+     */
+    IndexType IndexOf(const Type &aElement) const { return static_cast<IndexType>(&aElement - mArray); }
+
+    /**
+     * This method finds the first match of a given entry in the array.
+     *
+     * This method uses `==` operator on `Type` to compare the array element with @p aEntry. The returned pointer is
+     * valid only while the `Array` remains unchanged.
+     *
+     * @param[in] aEntry   The entry to search for within the array.
+     *
+     * @returns A pointer to matched array element, or `nullptr` if a match could not be found.
+     *
+     */
+    Type *Find(const Type &aEntry) { return AsNonConst(AsConst(this)->Find(aEntry)); }
+
+    /**
+     * This method finds the first match of a given entry in the array.
+     *
+     * This method uses `==` operator to compare the array elements with @p aEntry. The returned pointer is valid only
+     * while the `Array` remains unchanged.
+     *
+     * @param[in] aEntry   The entry to search for within the array.
+     *
+     * @returns A pointer to matched array element, or `nullptr` if a match could not be found.
+     *
+     */
+    const Type *Find(const Type &aEntry) const
+    {
+        const Type *matched = nullptr;
+
+        for (const Type &element : *this)
+        {
+            if (element == aEntry)
+            {
+                matched = &element;
+                break;
+            }
+        }
+
+        return matched;
+    }
+
+    /**
+     * This method indicates whether or not a match to given entry exists in the array.
+     *
+     * This method uses `==` operator on `Type` to compare the array elements with @p aEntry.
+     *
+     * @param[in] aEntry   The entry to search for within the array.
+     *
+     * @retval TRUE   The array contains a matching element with @p aEntry.
+     * @retval FALSE  The array does not contain a matching element with @p aEntry.
+     *
+     */
+    bool Contains(const Type &aEntry) const { return Find(aEntry) != nullptr; }
+
+    /**
+     * This template method finds the first element in the array matching a given indicator.
+     *
+     * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
+     * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
+     * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
+     *
+     *     bool Type::Matches(const Indicator &aIndicator) const
+     *
+     * The returned pointer is valid only while the `Array` remains unchanged.
+     *
+     * @param[in]  aIndicator  An indicator to match with elements in the array.
+     *
+     * @returns A pointer to the matched array element, or `nullptr` if a match could not be found.
+     *
+     */
+    template <typename Indicator> Type *FindMatching(const Indicator &aIndicator)
+    {
+        return AsNonConst(AsConst(this)->FindMatching(aIndicator));
+    }
+
+    /**
+     * This template method finds the first element in the array matching a given indicator.
+     *
+     * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
+     * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
+     * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
+     *
+     *     bool Type::Matches(const Indicator &aIndicator) const
+     *
+     * The returned pointer is valid only while the `Array` remains unchanged.
+     *
+     * @param[in]  aIndicator  An indicator to match with elements in the array.
+     *
+     * @returns A pointer to the matched array element, or `nullptr` if a match could not be found.
+     *
+     */
+    template <typename Indicator> const Type *FindMatching(const Indicator &aIndicator) const
+    {
+        const Type *matched = nullptr;
+
+        for (const Type &element : *this)
+        {
+            if (element.Matches(aIndicator))
+            {
+                matched = &element;
+                break;
+            }
+        }
+
+        return matched;
+    }
+
+    /**
+     * This template method indicates whether or not the array contains an element matching a given indicator.
+     *
+     * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements
+     * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each
+     * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly:
+     *
+     *     bool Type::Matches(const Indicator &aIndicator) const
+     *
+     * @param[in]  aIndicator  An indicator to match with elements in the array.
+     *
+     * @retval TRUE   The array contains a matching element with @p aIndicator.
+     * @retval FALSE  The array does not contain a matching element with @p aIndicator.
+     *
+     */
+    template <typename Indicator> bool ContainsMatching(const Indicator &aIndicator) const
+    {
+        return FindMatching(aIndicator) != nullptr;
+    }
+
+    // The following methods are intended to support range-based `for`
+    // loop iteration over the array elements and should not be used
+    // directly.
+
+    Type *      begin(void) { return (mLength > 0) ? mArray : nullptr; }
+    Type *      end(void) { return (mLength > 0) ? &mArray[mLength] : nullptr; }
+    const Type *begin(void) const { return (mLength > 0) ? mArray : nullptr; }
+    const Type *end(void) const { return (mLength > 0) ? &mArray[mLength] : nullptr; }
+
+    Array(const Array &) = delete;
+    Array &operator=(const Array &) = delete;
+
+private:
+    Error Allocate(IndexType aCapacity)
+    {
+        Error error = kErrorNone;
+        Type *newArray;
+
+        VerifyOrExit((aCapacity != mCapacity) && (aCapacity >= mLength));
+        newArray = static_cast<Type *>(Heap::CAlloc(aCapacity, sizeof(Type)));
+        VerifyOrExit(newArray != nullptr, error = kErrorNoBufs);
+
+        for (IndexType index = 0; index < mLength; index++)
+        {
+            new (&newArray[index]) Type(static_cast<Type &&>(mArray[index]));
+            mArray[index].~Type();
+        }
+
+        Heap::Free(mArray);
+        mArray    = newArray;
+        mCapacity = aCapacity;
+
+    exit:
+        return error;
+    }
+
+    Type *    mArray;
+    IndexType mLength;
+    IndexType mCapacity;
+};
+
+} // namespace Heap
+} // namespace ot
+
+#endif // HEAP_ARRAY_HPP_
diff --git a/src/core/common/instance.cpp b/src/core/common/instance.cpp
index fb543e4..af453ae 100644
--- a/src/core/common/instance.cpp
+++ b/src/core/common/instance.cpp
@@ -35,7 +35,6 @@
 
 #include <openthread/platform/misc.h>
 
-#include "common/logging.hpp"
 #include "common/new.hpp"
 #include "radio/trel_link.hpp"
 #include "utils/heap.hpp"
@@ -59,7 +58,7 @@
 #endif
 
 #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
-otLogLevel Instance::sLogLevel = static_cast<otLogLevel>(OPENTHREAD_CONFIG_LOG_LEVEL_INIT);
+LogLevel Instance::sLogLevel = static_cast<LogLevel>(OPENTHREAD_CONFIG_LOG_LEVEL_INIT);
 #endif
 
 Instance::Instance(void)
@@ -190,7 +189,7 @@
     // Restore datasets and network information
 
     Get<Settings>().Init();
-    IgnoreError(Get<Mle::MleRouter>().Restore());
+    Get<Mle::MleRouter>().Restore();
 
 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
     Get<Trel::Link>().AfterInit();
@@ -234,6 +233,7 @@
 }
 
 #if OPENTHREAD_MTD || OPENTHREAD_FTD
+
 void Instance::FactoryReset(void)
 {
     Get<Settings>().Wipe();
@@ -251,6 +251,55 @@
     return error;
 }
 
+void Instance::GetBufferInfo(BufferInfo &aInfo)
+{
+    aInfo.Clear();
+
+    aInfo.mTotalBuffers = Get<MessagePool>().GetTotalBufferCount();
+    aInfo.mFreeBuffers  = Get<MessagePool>().GetFreeBufferCount();
+
+    Get<MeshForwarder>().GetSendQueue().GetInfo(aInfo.m6loSendQueue);
+    Get<MeshForwarder>().GetReassemblyQueue().GetInfo(aInfo.m6loReassemblyQueue);
+    Get<Ip6::Ip6>().GetSendQueue().GetInfo(aInfo.mIp6Queue);
+
+#if OPENTHREAD_FTD
+    Get<Ip6::Mpl>().GetBufferedMessageSet().GetInfo(aInfo.mMplQueue);
+#endif
+
+    Get<Mle::MleRouter>().GetMessageQueue().GetInfo(aInfo.mMleQueue);
+
+    Get<Tmf::Agent>().GetRequestMessages().GetInfo(aInfo.mCoapQueue);
+    Get<Tmf::Agent>().GetCachedResponses().GetInfo(aInfo.mCoapQueue);
+
+#if OPENTHREAD_CONFIG_DTLS_ENABLE
+    Get<Coap::CoapSecure>().GetRequestMessages().GetInfo(aInfo.mCoapSecureQueue);
+    Get<Coap::CoapSecure>().GetCachedResponses().GetInfo(aInfo.mCoapSecureQueue);
+#endif
+
+#if OPENTHREAD_CONFIG_COAP_API_ENABLE
+    GetApplicationCoap().GetRequestMessages().GetInfo(aInfo.mApplicationCoapQueue);
+    GetApplicationCoap().GetCachedResponses().GetInfo(aInfo.mApplicationCoapQueue);
+#endif
+}
+
 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
 
+#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
+
+void Instance::SetLogLevel(LogLevel aLogLevel)
+{
+    if (aLogLevel != sLogLevel)
+    {
+        sLogLevel = aLogLevel;
+        otPlatLogHandleLevelChanged(sLogLevel);
+    }
+}
+
+extern "C" OT_TOOL_WEAK void otPlatLogHandleLevelChanged(otLogLevel aLogLevel)
+{
+    OT_UNUSED_VARIABLE(aLogLevel);
+}
+
+#endif
+
 } // namespace ot
diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp
index fa1b26f..386755f 100644
--- a/src/core/common/instance.hpp
+++ b/src/core/common/instance.hpp
@@ -40,17 +40,18 @@
 #include <stdint.h>
 
 #include <openthread/heap.h>
-#include <openthread/platform/logging.h>
 #if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE
 #include <openthread/platform/memory.h>
 #endif
 
+#include "common/array.hpp"
 #include "common/as_core_type.hpp"
 #include "common/error.hpp"
 #include "common/extension.hpp"
+#include "common/log.hpp"
 #include "common/message.hpp"
 #include "common/non_copyable.hpp"
-#include "common/random_manager.hpp"
+#include "common/random.hpp"
 #include "common/tasklet.hpp"
 #include "common/time_ticker.hpp"
 #include "common/timer.hpp"
@@ -70,6 +71,8 @@
 #include "crypto/mbedtls.hpp"
 #include "meshcop/border_agent.hpp"
 #include "meshcop/dataset_updater.hpp"
+#include "meshcop/extended_panid.hpp"
+#include "meshcop/network_name.hpp"
 #include "net/ip6.hpp"
 #include "thread/announce_sender.hpp"
 #include "thread/link_metrics.hpp"
@@ -112,15 +115,23 @@
 class Instance : public otInstance, private NonCopyable
 {
 public:
+    /**
+     * This type represents the message buffer information (number of messages/buffers in all OT stack message queues).
+     *
+     */
+    class BufferInfo : public otBufferInfo, public Clearable<BufferInfo>
+    {
+    };
+
 #if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
     /**
       * This static method initializes the OpenThread instance.
       *
       * This function must be called before any other calls on OpenThread instance.
       *
-      * @param[in]    aBuffer      The buffer for OpenThread to use for allocating the Instance.
-      * @param[inout] aBufferSize  On input, the size of `aBuffer`. On output, if not enough space for `Instance`, the
-                                   number of bytes required for `Instance`.
+      * @param[in]     aBuffer      The buffer for OpenThread to use for allocating the Instance.
+      * @param[in,out] aBufferSize  On input, the size of `aBuffer`. On output, if not enough space for `Instance`, the
+                                    number of bytes required for `Instance`.
       *
       * @returns  A pointer to the new OpenThread instance.
       *
@@ -180,14 +191,14 @@
      * @returns The log level.
      *
      */
-    static otLogLevel GetLogLevel(void)
+    static LogLevel GetLogLevel(void)
 #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
     {
         return sLogLevel;
     }
 #else
     {
-        return static_cast<otLogLevel>(OPENTHREAD_CONFIG_LOG_LEVEL);
+        return static_cast<LogLevel>(OPENTHREAD_CONFIG_LOG_LEVEL);
     }
 #endif
 
@@ -198,11 +209,7 @@
      * @param[in] aLogLevel  A log level.
      *
      */
-    static void SetLogLevel(otLogLevel aLogLevel)
-    {
-        OT_ASSERT(aLogLevel <= OT_LOG_LEVEL_DEBG && aLogLevel >= OT_LOG_LEVEL_NONE);
-        sLogLevel = aLogLevel;
-    }
+    static void SetLogLevel(LogLevel aLogLevel);
 #endif
 
     /**
@@ -284,6 +291,14 @@
     static bool IsDnsNameCompressionEnabled(void) { return sDnsNameCompressionEnabled; }
 #endif
 
+    /**
+     * This method retrieves the the Message Buffer information.
+     *
+     * @param[out]  aInfo  A `BufferInfo` where information is written.
+     *
+     */
+    void GetBufferInfo(BufferInfo &aInfo);
+
 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
 
     /**
@@ -318,7 +333,7 @@
 #endif
 
 #if OPENTHREAD_MTD || OPENTHREAD_FTD
-    // RandomManager is initialized before other objects. Note that it
+    // Random::Manager is initialized before other objects. Note that it
     // requires MbedTls which itself may use Heap.
 #if !OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE
     static Utils::Heap sHeap;
@@ -326,7 +341,7 @@
     Crypto::MbedTls mMbedTls;
 #endif // OPENTHREAD_MTD || OPENTHREAD_FTD
 
-    RandomManager mRandomManager;
+    Random::Manager mRandomManager;
 
     // Radio is initialized before other member variables
     // (particularly, SubMac and Mac) to allow them to use its methods
@@ -395,7 +410,7 @@
 #endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
 
 #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
-    static otLogLevel sLogLevel;
+    static LogLevel sLogLevel;
 #endif
 #if OPENTHREAD_ENABLE_VENDOR_EXTENSION
     Extension::ExtensionBase &mExtension;
@@ -411,6 +426,7 @@
 };
 
 DefineCoreType(otInstance, Instance);
+DefineCoreType(otBufferInfo, Instance::BufferInfo);
 
 // Specializations of the `Get<Type>()` method.
 
@@ -687,12 +703,22 @@
 }
 #endif
 
-template <> inline MeshCoP::ActiveDataset &Instance::Get(void)
+template <> inline MeshCoP::ExtendedPanIdManager &Instance::Get(void)
+{
+    return mThreadNetif.mExtendedPanIdManager;
+}
+
+template <> inline MeshCoP::NetworkNameManager &Instance::Get(void)
+{
+    return mThreadNetif.mNetworkNameManager;
+}
+
+template <> inline MeshCoP::ActiveDatasetManager &Instance::Get(void)
 {
     return mThreadNetif.mActiveDataset;
 }
 
-template <> inline MeshCoP::PendingDataset &Instance::Get(void)
+template <> inline MeshCoP::PendingDatasetManager &Instance::Get(void)
 {
     return mThreadNetif.mPendingDataset;
 }
diff --git a/src/core/common/log.cpp b/src/core/common/log.cpp
new file mode 100644
index 0000000..a0a126a
--- /dev/null
+++ b/src/core/common/log.cpp
@@ -0,0 +1,266 @@
+/*
+ *  Copyright (c) 2017-2022, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file implements the logging related functions.
+ */
+
+#include "log.hpp"
+
+#include <ctype.h>
+
+#include <openthread/platform/logging.h>
+
+#include "common/code_utils.hpp"
+#include "common/instance.hpp"
+#include "common/string.hpp"
+
+/*
+ * Verify debug UART dependency.
+ *
+ * It is reasonable to only enable the debug UART and not enable logs to the DEBUG UART.
+ */
+#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && (!OPENTHREAD_CONFIG_ENABLE_DEBUG_UART)
+#error "OPENTHREAD_CONFIG_ENABLE_DEBUG_UART_LOG requires OPENTHREAD_CONFIG_ENABLE_DEBUG_UART"
+#endif
+
+#if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME && !OPENTHREAD_CONFIG_UPTIME_ENABLE
+#error "OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME requires OPENTHREAD_CONFIG_UPTIME_ENABLE"
+#endif
+
+#if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME && OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
+#error "OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME is not supported under OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE"
+#endif
+
+namespace ot {
+
+#if OT_SHOULD_LOG
+
+template <LogLevel kLogLevel> void Logger::LogAtLevel(const char *aModuleName, const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    LogVarArgs(aModuleName, kLogLevel, aFormat, args);
+    va_end(args);
+}
+
+// Explicit instantiations
+template void Logger::LogAtLevel<kLogLevelNone>(const char *aModuleName, const char *aFormat, ...);
+template void Logger::LogAtLevel<kLogLevelCrit>(const char *aModuleName, const char *aFormat, ...);
+template void Logger::LogAtLevel<kLogLevelWarn>(const char *aModuleName, const char *aFormat, ...);
+template void Logger::LogAtLevel<kLogLevelNote>(const char *aModuleName, const char *aFormat, ...);
+template void Logger::LogAtLevel<kLogLevelInfo>(const char *aModuleName, const char *aFormat, ...);
+template void Logger::LogAtLevel<kLogLevelDebg>(const char *aModuleName, const char *aFormat, ...);
+
+void Logger::LogInModule(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, ...)
+{
+    va_list args;
+
+    va_start(args, aFormat);
+    LogVarArgs(aModuleName, aLogLevel, aFormat, args);
+    va_end(args);
+}
+
+void Logger::LogVarArgs(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, va_list aArgs)
+{
+    static const char kModuleNamePadding[] = "--------------";
+
+    ot::String<OPENTHREAD_CONFIG_LOG_MAX_SIZE> logString;
+
+    static_assert(sizeof(kModuleNamePadding) == kMaxLogModuleNameLength + 1, "Padding string is not correct");
+
+#if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME
+    ot::Uptime::UptimeToString(ot::Instance::Get().Get<ot::Uptime>().GetUptime(), logString);
+    logString.Append(" ");
+#endif
+
+#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
+    VerifyOrExit(Instance::GetLogLevel() >= aLogLevel);
+#endif
+
+#if OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL
+    {
+        static const char kLevelChars[] = {
+            '-', /* kLogLevelNone */
+            'C', /* kLogLevelCrit */
+            'W', /* kLogLevelWarn */
+            'N', /* kLogLevelNote */
+            'I', /* kLogLevelInfo */
+            'D', /* kLogLevelDebg */
+        };
+
+        logString.Append("[%c] ", kLevelChars[aLogLevel]);
+    }
+#endif
+
+    logString.Append("%.*s%s: ", kMaxLogModuleNameLength, aModuleName,
+                     &kModuleNamePadding[StringLength(aModuleName, kMaxLogModuleNameLength)]);
+
+    logString.AppendVarArgs(aFormat, aArgs);
+
+    logString.Append("%s", OPENTHREAD_CONFIG_LOG_SUFFIX);
+    otPlatLog(aLogLevel, OT_LOG_REGION_CORE, "%s", logString.AsCString());
+
+    ExitNow();
+
+exit:
+    return;
+}
+
+#if OPENTHREAD_CONFIG_LOG_PKT_DUMP
+
+template <LogLevel kLogLevel>
+void Logger::DumpAtLevel(const char *aModuleName, const char *aText, const void *aData, uint16_t aDataLength)
+{
+    DumpInModule(aModuleName, kLogLevel, aText, aData, aDataLength);
+}
+
+// Explicit instantiations
+template void Logger::DumpAtLevel<kLogLevelNone>(const char *aModuleName,
+                                                 const char *aText,
+                                                 const void *aData,
+                                                 uint16_t    aDataLength);
+template void Logger::DumpAtLevel<kLogLevelCrit>(const char *aModuleName,
+                                                 const char *aText,
+                                                 const void *aData,
+                                                 uint16_t    aDataLength);
+template void Logger::DumpAtLevel<kLogLevelWarn>(const char *aModuleName,
+                                                 const char *aText,
+                                                 const void *aData,
+                                                 uint16_t    aDataLength);
+template void Logger::DumpAtLevel<kLogLevelNote>(const char *aModuleName,
+                                                 const char *aText,
+                                                 const void *aData,
+                                                 uint16_t    aDataLength);
+template void Logger::DumpAtLevel<kLogLevelInfo>(const char *aModuleName,
+                                                 const char *aText,
+                                                 const void *aData,
+                                                 uint16_t    aDataLength);
+template void Logger::DumpAtLevel<kLogLevelDebg>(const char *aModuleName,
+                                                 const char *aText,
+                                                 const void *aData,
+                                                 uint16_t    aDataLength);
+
+void Logger::DumpLine(const char *aModuleName, LogLevel aLogLevel, const uint8_t *aData, const uint16_t aDataLength)
+{
+    ot::String<kStringLineLength> string;
+
+    string.Append("|");
+
+    for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
+    {
+        if (i < aDataLength)
+        {
+            string.Append(" %02X", aData[i]);
+        }
+        else
+        {
+            string.Append(" ..");
+        }
+
+        if (!((i + 1) % 8))
+        {
+            string.Append(" |");
+        }
+    }
+
+    string.Append(" ");
+
+    for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
+    {
+        char c = '.';
+
+        if (i < aDataLength)
+        {
+            char byteAsChar = static_cast<char>(0x7f & aData[i]);
+
+            if (isprint(byteAsChar))
+            {
+                c = byteAsChar;
+            }
+        }
+
+        string.Append("%c", c);
+    }
+
+    LogInModule(aModuleName, aLogLevel, "%s", string.AsCString());
+}
+
+void Logger::DumpInModule(const char *aModuleName,
+                          LogLevel    aLogLevel,
+                          const char *aText,
+                          const void *aData,
+                          uint16_t    aDataLength)
+{
+    constexpr uint16_t kWidth         = 72;
+    constexpr uint16_t kTextSuffixLen = sizeof("[ len=000]") - 1;
+
+    uint16_t                      txtLen = StringLength(aText, kWidth - kTextSuffixLen) + kTextSuffixLen;
+    ot::String<kStringLineLength> string;
+
+    VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
+
+    for (uint16_t i = 0; i < static_cast<uint16_t>((kWidth - txtLen) / 2); i++)
+    {
+        string.Append("=");
+    }
+
+    string.Append("[%s len=%03u]", aText, aDataLength);
+
+    for (uint16_t i = 0; i < static_cast<uint16_t>(kWidth - txtLen - (kWidth - txtLen) / 2); i++)
+    {
+        string.Append("=");
+    }
+
+    LogInModule(aModuleName, aLogLevel, "%s", string.AsCString());
+
+    for (uint16_t i = 0; i < aDataLength; i += kDumpBytesPerLine)
+    {
+        DumpLine(aModuleName, aLogLevel, static_cast<const uint8_t *>(aData) + i,
+                 OT_MIN((aDataLength - i), kDumpBytesPerLine));
+    }
+
+    string.Clear();
+
+    for (uint16_t i = 0; i < kWidth; i++)
+    {
+        string.Append("-");
+    }
+
+    LogInModule(aModuleName, aLogLevel, "%s", string.AsCString());
+
+exit:
+    return;
+}
+#endif // OPENTHREAD_CONFIG_LOG_PKT_DUMP
+
+#endif // OT_SHOULD_LOG
+
+} // namespace ot
diff --git a/src/core/common/log.hpp b/src/core/common/log.hpp
new file mode 100644
index 0000000..0870e06
--- /dev/null
+++ b/src/core/common/log.hpp
@@ -0,0 +1,379 @@
+/*
+ *  Copyright (c) 2022, The OpenThread Authors.
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the copyright holder nor the
+ *     names of its contributors may be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ *  POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ *   This file includes logging related  definitions.
+ */
+
+#ifndef LOG_HPP_
+#define LOG_HPP_
+
+#include "openthread-core-config.h"
+
+#include <openthread/logging.h>
+#include <openthread/platform/logging.h>
+#include <openthread/platform/toolchain.h>
+
+namespace ot {
+
+/**
+ * @def OT_SHOULD_LOG
+ *
+ * This definition indicates whether or not logging is enabled.
+ *
+ */
+#define OT_SHOULD_LOG (OPENTHREAD_CONFIG_LOG_OUTPUT != OPENTHREAD_CONFIG_LOG_OUTPUT_NONE)
+
+/**
+ * This macro indicates whether the OpenThread logging is enabled at a given log level.
+ *
+ * @param[in] aLevel   The log level to check.
+ *
+ * @returns TRUE if logging is enabled at @p aLevel, FALSE otherwise.
+ *
+ */
+#define OT_SHOULD_LOG_AT(aLevel) (OT_SHOULD_LOG && (OPENTHREAD_CONFIG_LOG_LEVEL >= (aLevel)))
+
+/**
+ * This enumeration represents the log level.
+ *
+ */
+enum LogLevel : uint8_t
+{
+    kLogLevelNone = OT_LOG_LEVEL_NONE, ///< None (disable logs)
+    kLogLevelCrit = OT_LOG_LEVEL_CRIT, ///< Critical log level
+    kLogLevelWarn = OT_LOG_LEVEL_WARN, ///< Warning log level
+    kLogLevelNote = OT_LOG_LEVEL_NOTE, ///< Note log level
+    kLogLevelInfo = OT_LOG_LEVEL_INFO, ///< Info log level
+    kLogLevelDebg = OT_LOG_LEVEL_DEBG, ///< Debug log level
+};
+
+constexpr uint8_t kMaxLogModuleNameLength = 14; ///< Maximum module name length
+
+#if OT_SHOULD_LOG && (OPENTHREAD_CONFIG_LOG_LEVEL != OT_LOG_LEVEL_NONE)
+/**
+ * This macro registers log module name.
+ *
+ * This macro is used in a `cpp` file to register the log module name for that file before using any other logging
+ * functions or macros (e.g., `LogInfo()` or `DumpInfo()`, ...) in the file.
+ *
+ * @param[in] aName  The log module name string (MUST be shorter than `kMaxLogModuleNameLength`).
+ *
+ */
+#define RegisterLogModule(aName)                                     \
+    constexpr char kLogModuleName[] = aName;                         \
+    namespace {                                                      \
+    /* Defining this type to silence "unused constant" warning/error \
+     * for `kLogModuleName` under any log level config.              \
+     */                                                              \
+    using DummyType = char[sizeof(kLogModuleName)];                  \
+    }                                                                \
+    static_assert(sizeof(kLogModuleName) <= kMaxLogModuleNameLength + 1, "Log module name is too long")
+
+#else
+#define RegisterLogModule(aName) static_assert(true, "Consume the required semi-colon at the end of macro")
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_CRIT)
+/**
+ * This macro emits a log message at critical log level.
+ *
+ * @param[in]  ...   Arguments for the format specification.
+ *
+ */
+#define LogCrit(...) Logger::Log<kLogLevelCrit, kLogModuleName>(__VA_ARGS__)
+#else
+#define LogCrit(...)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
+/**
+ * This macro emits a log message at warning log level.
+ *
+ * @param[in]  ...   Arguments for the format specification.
+ *
+ */
+#define LogWarn(...) Logger::Log<kLogLevelWarn, kLogModuleName>(__VA_ARGS__)
+#else
+#define LogWarn(...)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE)
+/**
+ * This macro emits a log message at note log level.
+ *
+ * @param[in]  ...   Arguments for the format specification.
+ *
+ */
+#define LogNote(...) Logger::Log<kLogLevelNote, kLogModuleName>(__VA_ARGS__)
+#else
+#define LogNote(...)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
+/**
+ * This macro emits a log message at info log level.
+ *
+ * @param[in]  ...   Arguments for the format specification.
+ *
+ */
+#define LogInfo(...) Logger::Log<kLogLevelInfo, kLogModuleName>(__VA_ARGS__)
+#else
+#define LogInfo(...)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG)
+/**
+ * This macro emits a log message at debug log level.
+ *
+ * @param[in]  ...   Arguments for the format specification.
+ *
+ */
+#define LogDebg(...) Logger::Log<kLogLevelDebg, kLogModuleName>(__VA_ARGS__)
+#else
+#define LogDebg(...)
+#endif
+
+#if OT_SHOULD_LOG
+/**
+ * This macro emits a log message at a given log level.
+ *
+ * @param[in] aLogLevel  The log level to use.
+ * @param[in] ...        Argument for the format specification.
+ *
+ */
+#define LogAt(aLogLevel, ...) Logger::LogInModule(kLogModuleName, aLogLevel, __VA_ARGS__)
+#else
+#define LogAt(aLogLevel, ...)
+#endif
+
+#if OT_SHOULD_LOG
+/**
+ * This macro emits a log message independent of the configured log level.
+ *
+ * @param[in]  ...   Arguments for the format specification.
+ *
+ */
+#define LogAlways(...) Logger::LogInModule("", kLogLevelNone, __VA_ARGS__)
+#else
+#define LogAlways(...)
+#endif
+
+#if OT_SHOULD_LOG && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
+/**
+ * This macro emit a log message for the certification test.
+ *
+ * @param[in]  ...  Arguments for the format specification.
+ *
+ */
+#define LogCert(...) LogAlways(__VA_ARGS__)
+#else
+#define LogCert(...)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_CRIT) && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+/**
+ * This macro generates a memory dump at log level critical.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+#define DumpCrit(aText, aData, aDataLength) Logger::Dump<kLogLevelCrit, kLogModuleName>(aText, aData, aDataLength)
+#else
+#define DumpCrit(aText, aData, aDataLength)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+/**
+ * This macro generates a memory dump at log level warning.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+#define DumpWarn(aText, aData, aDataLength) Logger::Dump<kLogLevelWarn, kLogModuleName>(aText, aData, aDataLength)
+#else
+#define DumpWarn(aText, aData, aDataLength)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE) && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+/**
+ * This macro generates a memory dump at log level note.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+#define DumpNote(aText, aData, aDataLength) Logger::Dump<kLogLevelNote, kLogModuleName>(aText, aData, aDataLength)
+#else
+#define DumpNote(aText, aData, aDataLength)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+/**
+ * This macro generates a memory dump at log level info.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+#define DumpInfo(aText, aData, aDataLength) Logger::Dump<kLogLevelInfo, kLogModuleName>(aText, aData, aDataLength)
+#else
+#define DumpInfo(aText, aData, aDataLength)
+#endif
+
+#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG) && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+/**
+ * This macro generates a memory dump at log level debug.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+#define DumpDebg(aText, aData, aDataLength) Logger::Dump<kLogLevelDebg, kLogModuleName>(aText, aData, aDataLength)
+#else
+#define DumpDebg(aText, aData, aDataLength)
+#endif
+
+#if OT_SHOULD_LOG && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+/**
+ * This macro generates a memory dump independent of the configured log level.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+#define DumpAlways(aText, aData, aDataLength) Logger::DumpInModule("", kLogLevelNone, aText, aData, aDataLength)
+#endif
+
+#if OT_SHOULD_LOG && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE && OPENTHREAD_CONFIG_LOG_PKT_DUMP
+/**
+ * This macro generates a memory dump for certification test.
+ *
+ * @param[in]  aText         A string that is printed before the bytes.
+ * @param[in]  aData         A pointer to the data buffer.
+ * @param[in]  aDataLength   Number of bytes in @p aData.
+ *
+ */
+#define DumpCert(aText, aData, aDataLength) DumpAlways(aText, aData, aDataLength)
+#else
+#define DumpCert(aText, aData, aDataLength)
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------
+
+#if OT_SHOULD_LOG
+
+class Logger
+{
+    // The `Logger` class implements the logging methods.
+    //
+    // The `Logger` methods are not intended to be directly used
+    // and instead the logging macros should be used.
+
+public:
+    template <LogLevel kLogLevel, const char *kModuleName, typename... Args>
+    static void Log(const char *aFormat, Args... aArgs)
+    {
+        LogAtLevel<kLogLevel>(kModuleName, aFormat, aArgs...);
+    }
+
+    static void LogInModule(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, ...);
+
+    template <LogLevel kLogLevel> static void LogAtLevel(const char *aModuleName, const char *aFormat, ...);
+    static void LogVarArgs(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, va_list aArgs);
+
+#if OPENTHREAD_CONFIG_LOG_PKT_DUMP
+    static constexpr uint8_t kStringLineLength = 80;
+    static constexpr uint8_t kDumpBytesPerLine = 16;
+
+    template <LogLevel kLogLevel, const char *kModuleName>
+    static void Dump(const char *aText, const void *aData, uint16_t aDataLength)
+    {
+        DumpAtLevel<kLogLevel>(kModuleName, aText, aData, aDataLength);
+    }
+
+    static void DumpInModule(const char *aModuleName,
+                             LogLevel    aLogLevel,
+                             const char *aText,
+                             const void *aData,
+                             uint16_t    aDataLength);
+
+    template <LogLevel kLogLevel>
+    static void DumpAtLevel(const char *aModuleName, const char *aText, const void *aData, uint16_t aDataLength);
+
+    static void DumpLine(const char *aModuleName, LogLevel aLogLevel, const uint8_t *aData, uint16_t aDataLength);
+#endif
+};
+
+extern template void Logger::LogAtLevel<kLogLevelNone>(const char *aModuleName, const char *aFormat, ...);
+extern template void Logger::LogAtLevel<kLogLevelCrit>(const char *aModuleName, const char *aFormat, ...);
+extern template void Logger::LogAtLevel<kLogLevelWarn>(const char *aModuleName, const char *aFormat, ...);
+extern template void Logger::LogAtLevel<kLogLevelNote>(const char *aModuleName, const char *aFormat, ...);
+extern template void Logger::LogAtLevel<kLogLevelInfo>(const char *aModuleName, const char *aFormat, ...);
+extern template void Logger::LogAtLevel<kLogLevelDebg>(const char *aModuleName, const char *aFormat, ...);
+
+#if OPENTHREAD_CONFIG_LOG_PKT_DUMP
+extern template void Logger::DumpAtLevel<kLogLevelNone>(const char *aModuleName,
+                                                        const char *aText,
+                                                        const void *aData,
+                                                        uint16_t    aDataLength);
+extern template void Logger::DumpAtLevel<kLogLevelCrit>(const char *aModuleName,
+                                                        const char *aText,
+                                                        const void *aData,
+                                                        uint16_t    aDataLength);
+extern template void Logger::DumpAtLevel<kLogLevelWarn>(const char *aModuleName,
+                                                        const char *aText,
+                                                        const void *aData,
+                                                        uint16_t    aDataLength);
+extern template void Logger::DumpAtLevel<kLogLevelNote>(const char *aModuleName,
+                                                        const char *aText,
+                                                        const void *aData,
+                                                        uint16_t    aDataLength);
+extern template void Logger::DumpAtLevel<kLogLevelInfo>(const char *aModuleName,
+                                                        const char *aText,
+                                                        const void *aData,
+                                                        uint16_t    aDataLength);
+extern template void Logger::DumpAtLevel<kLogLevelDebg>(const char *aModuleName,
+                                                        const char *aText,
+                                                        const void *aData,
+                                                        uint16_t    aDataLength);
+#endif // OPENTHREAD_CONFIG_LOG_PKT_DUMP
+#endif // OT_SHOULD_LOG
+
+} // namespace ot
+
+#endif // LOG_HPP_
diff --git a/src/core/common/logging.cpp b/src/core/common/logging.cpp
deleted file mode 100644
index af45094..0000000
--- a/src/core/common/logging.cpp
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- *  Copyright (c) 2016, The OpenThread Authors.
- *  All rights reserved.
- *
- *  Redistribution and use in source and binary forms, with or without
- *  modification, are permitted provided that the following conditions are met:
- *  1. Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer.
- *  2. Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the
- *     documentation and/or other materials provided with the distribution.
- *  3. Neither the name of the copyright holder nor the
- *     names of its contributors may be used to endorse or promote products
- *     derived from this software without specific prior written permission.
- *
- *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- *  POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @file
- *   This file implements the logging related functions.
- */
-
-#include "logging.hpp"
-
-#include "common/code_utils.hpp"
-#include "common/instance.hpp"
-#include "common/string.hpp"
-
-/*
- * Verify debug uart dependency.
- *
- * It is reasonable to only enable the debug uart and not enable logs to the DEBUG uart.
- */
-#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && (!OPENTHREAD_CONFIG_ENABLE_DEBUG_UART)
-#error "OPENTHREAD_CONFIG_ENABLE_DEBUG_UART_LOG requires OPENTHREAD_CONFIG_ENABLE_DEBUG_UART"
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME && !OPENTHREAD_CONFIG_UPTIME_ENABLE
-#error "OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME requires OPENTHREAD_CONFIG_UPTIME_ENABLE"
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME && OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE
-#error "OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME is not supported under OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE"
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static void Log(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs)
-{
-    ot::String<OPENTHREAD_CONFIG_LOG_MAX_SIZE> logString;
-
-#if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME
-    ot::Uptime::UptimeToString(ot::Instance::Get().Get<ot::Uptime>().GetUptime(), logString);
-    logString.Append(" ");
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
-    VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL
-
-    {
-        static const char *const kLevelStrings[] = {
-            "NONE", "CRIT", "WARN", "NOTE", "INFO", "DEBG",
-        };
-
-        uint16_t index = ((aLogLevel >= 0) && (aLogLevel < static_cast<int>(OT_ARRAY_LENGTH(kLevelStrings))))
-                             ? static_cast<uint16_t>(aLogLevel)
-                             : 0;
-
-        logString.Append("[%s]", kLevelStrings[index]);
-    }
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_PREPEND_REGION
-    {
-        static const char *const kRegionStrings[] = {
-            "-------", "API----", "MLE----", "ARP----", "N-DATA-", "ICMP---", "IP6----", "TCP----",
-            "MAC----", "MEM----", "NCP----", "MESH-CP", "DIAG---", "PLAT---", "COAP---", "CLI----",
-            "CORE---", "UTIL---", "BBR----", "MLR----", "DUA----", "BR-----", "SRP----", "DNS----",
-        };
-
-        uint16_t index = (aLogRegion < OT_ARRAY_LENGTH(kRegionStrings)) ? static_cast<uint16_t>(aLogRegion) : 0;
-
-        logString.Append("-%s-: ", kRegionStrings[index]);
-    }
-#elif OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL
-    logString.Append(": ");
-#endif
-
-    logString.AppendVarArgs(aFormat, aArgs);
-    otPlatLog(aLogLevel, aLogRegion, "%s" OPENTHREAD_CONFIG_LOG_SUFFIX, logString.AsCString());
-
-#if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE
-exit:
-    return;
-#endif
-}
-
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_CRIT
-void _otLogCrit(otLogRegion aRegion, const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    Log(OT_LOG_LEVEL_CRIT, aRegion, aFormat, args);
-    va_end(args);
-}
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN
-void _otLogWarn(otLogRegion aRegion, const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    Log(OT_LOG_LEVEL_WARN, aRegion, aFormat, args);
-    va_end(args);
-}
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE
-void _otLogNote(otLogRegion aRegion, const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    Log(OT_LOG_LEVEL_NOTE, aRegion, aFormat, args);
-    va_end(args);
-}
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO
-void _otLogInfo(otLogRegion aRegion, const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    Log(OT_LOG_LEVEL_INFO, aRegion, aFormat, args);
-    va_end(args);
-}
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG
-void _otLogDebg(otLogRegion aRegion, const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    Log(OT_LOG_LEVEL_DEBG, aRegion, aFormat, args);
-    va_end(args);
-}
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_MAC
-void otLogMac(otLogLevel aLogLevel, const char *aFormat, ...)
-{
-    va_list args;
-
-    VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
-
-    va_start(args, aFormat);
-    Log(aLogLevel, OT_LOG_REGION_MAC, aFormat, args);
-    va_end(args);
-
-exit:
-    return;
-}
-
-void otDumpMacFrame(otLogLevel aLogLevel, const char *aId, const void *aBuf, const size_t aLength)
-{
-    constexpr uint8_t kFixedStringPart = 10; // strlen(" seqno=000")
-    constexpr uint8_t kSeqnoIdx        = 2;  // index of the sequence number within aBuf
-
-    size_t idLength = strlen(aId) + kFixedStringPart + 1; // allow for the '\0' character
-    char   newId[25];
-
-    VerifyOrExit(idLength <= sizeof(newId));
-    snprintf(newId, idLength, "%s seqno=%03u", aId, static_cast<const uint8_t *>(aBuf)[kSeqnoIdx]);
-    otDump(aLogLevel, OT_LOG_REGION_MAC, newId, aBuf, aLength);
-exit:
-    return;
-}
-#endif
-
-#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-void otLogCertMeshCoP(const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    Log(OT_LOG_LEVEL_NONE, OT_LOG_REGION_MESH_COP, aFormat, args);
-    va_end(args);
-}
-#endif
-
-#if OPENTHREAD_CONFIG_OTNS_ENABLE
-void otLogOtns(const char *aFormat, ...)
-{
-    va_list args;
-
-    va_start(args, aFormat);
-    Log(OT_LOG_LEVEL_NONE, OT_LOG_REGION_CORE, aFormat, args);
-    va_end(args);
-}
-#endif
-
-#if OPENTHREAD_CONFIG_LOG_PKT_DUMP
-
-static void otLogDump(otLogLevel aLogLevel, otLogRegion aRegion, const char *aFormat, ...)
-{
-    va_list args;
-
-    VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
-
-    va_start(args, aFormat);
-    Log(aLogLevel, aRegion, aFormat, args);
-    va_end(args);
-
-exit:
-    return;
-}
-
-static constexpr uint8_t kStringLineLength = 80;
-static constexpr uint8_t kDumpBytesPerLine = 16;
-
-static void DumpLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const uint8_t *aBytes, const size_t aLength)
-{
-    ot::String<kStringLineLength> string;
-
-    string.Append("|");
-
-    for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
-    {
-        if (i < aLength)
-        {
-            string.Append(" %02X", aBytes[i]);
-        }
-        else
-        {
-            string.Append(" ..");
-        }
-
-        if (!((i + 1) % 8))
-        {
-            string.Append(" |");
-        }
-    }
-
-    string.Append(" ");
-
-    for (uint8_t i = 0; i < kDumpBytesPerLine; i++)
-    {
-        char c = '.';
-
-        if (i < aLength)
-        {
-            char byteAsChar = static_cast<char>(0x7f & aBytes[i]);
-
-            if (isprint(byteAsChar))
-            {
-                c = byteAsChar;
-            }
-        }
-
-        string.Append("%c", c);
-    }
-
-    otLogDump(aLogLevel, aLogRegion, "%s", string.AsCString());
-}
-
-void otDump(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aId, const void *aBuf, const size_t aLength)
-{
-    constexpr uint8_t kWidth           = 72;
-    constexpr uint8_t kFixedStringPart = 10; // strlen("[ len=000]")
-
-    size_t                        idLen = strlen(aId) + kFixedStringPart;
-    ot::String<kStringLineLength> string;
-
-    VerifyOrExit(otLoggingGetLevel() >= aLogLevel);
-
-    for (size_t i = 0; i < (kWidth - idLen) / 2; i++)
-    {
-        string.Append("=");
-    }
-
-    string.Append("[%s len=%03u]", aId, static_cast<unsigned>(aLength));
-
-    for (size_t i = 0; i < kWidth - idLen - (kWidth - idLen) / 2; i++)
-    {
-        string.Append("=");
-    }
-
-    otLogDump(aLogLevel, aLogRegion, "%s", string.AsCString());
-
-    for (size_t i = 0; i < aLength; i += kDumpBytesPerLine)
-    {
-        DumpLine(aLogLevel, aLogRegion, static_cast<const uint8_t *>(aBuf) + i,
-                 OT_MIN((aLength - i), static_cast<size_t>(kDumpBytesPerLine)));
-    }
-
-    string.Clear();
-
-    for (size_t i = 0; i < kWidth; i++)
-    {
-        string.Append("-");
-    }
-
-    otLogDump(aLogLevel, aLogRegion, "%s", string.AsCString());
-
-exit:
-    return;
-}
-#else  // OPENTHREAD_CONFIG_LOG_PKT_DUMP
-void otDump(otLogLevel, otLogRegion, const char *, const void *, const size_t)
-{
-}
-#endif // OPENTHREAD_CONFIG_LOG_PKT_DUMP
-
-#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_NONE
-/* this provides a stub, in case something uses the function */
-void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...)
-{
-    OT_UNUSED_VARIABLE(aLogLevel);
-    OT_UNUSED_VARIABLE(aLogRegion);
-    OT_UNUSED_VARIABLE(aFormat);
-}
-#endif
-
-OT_TOOL_WEAK void otPlatLogLine(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogLine)
-{
-    otPlatLog(aLogLevel, aLogRegion, "%s", aLogLine);
-}
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/src/core/common/logging.hpp b/src/core/common/logging.hpp
index dc3167b..56016bc 100644
--- a/src/core/common/logging.hpp
+++ b/src/core/common/logging.hpp
@@ -36,2554 +36,7 @@
 
 #include "openthread-core-config.h"
 
-#include <ctype.h>
-#include <stdio.h>
-
 #include <openthread/logging.h>
 #include <openthread/platform/logging.h>
 
-#include "common/arg_macros.hpp"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * @def otLogCrit
- *
- * Logging at log level critical.
- *
- * @param[in]  aRegion   The log region.
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL < OT_LOG_LEVEL_CRIT
-#define otLogCrit(aRegion, ...)
-#else
-#define otLogCrit(aRegion, ...) _otLogCrit(aRegion, __VA_ARGS__)
-void _otLogCrit(otLogRegion aRegion, const char *aFormat, ...);
-#endif
-
-/**
- * @def otLogWarn
- *
- * Logging at log level warning.
- *
- * @param[in]  aRegion   The log region.
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL < OT_LOG_LEVEL_WARN
-#define otLogWarn(aRegion, ...)
-#else
-#define otLogWarn(aRegion, ...) _otLogWarn(aRegion, __VA_ARGS__)
-void _otLogWarn(otLogRegion aRegion, const char *aFormat, ...);
-#endif
-
-/**
- * @def otLogNote
- *
- * Logging at log level note
- *
- * @param[in]  aRegion   The log region.
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL < OT_LOG_LEVEL_NOTE
-#define otLogNote(aRegion, ...)
-#else
-#define otLogNote(aRegion, ...) _otLogNote(aRegion, __VA_ARGS__)
-void _otLogNote(otLogRegion aRegion, const char *aFormat, ...);
-#endif
-
-/**
- * @def otLogInfo
- *
- * Logging at log level info.
- *
- * @param[in]  aRegion   The log region.
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL < OT_LOG_LEVEL_INFO
-#define otLogInfo(aRegion, ...)
-#else
-#define otLogInfo(aRegion, ...) _otLogInfo(aRegion, __VA_ARGS__)
-void _otLogInfo(otLogRegion aRegion, const char *aFormat, ...);
-#endif
-
-/**
- * @def otLogDebg
- *
- * Logging at log level debug.
- *
- * @param[in]  aRegion   The log region.
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL < OT_LOG_LEVEL_DEBG
-#define otLogDebg(aRegion, ...)
-#else
-#define otLogDebg(aRegion, ...) _otLogDebg(aRegion, __VA_ARGS__)
-void _otLogDebg(otLogRegion aRegion, const char *aFormat, ...);
-#endif
-
-/**
- * @def otLogCritApi
- *
- * This function generates a log with level critical for the API region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnApi
- *
- * This function generates a log with level warning for the API region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteApi
- *
- * This function generates a log with level note for the API region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoApi
- *
- * This function generates a log with level info for the API region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgApi
- *
- * This function generates a log with level debug for the API region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_API
-#define otLogCritApi(...) otLogCrit(OT_LOG_REGION_API, __VA_ARGS__)
-#define otLogWarnApi(...) otLogWarn(OT_LOG_REGION_API, __VA_ARGS__)
-#define otLogNoteApi(...) otLogNote(OT_LOG_REGION_API, __VA_ARGS__)
-#define otLogInfoApi(...) otLogInfo(OT_LOG_REGION_API, __VA_ARGS__)
-#define otLogDebgApi(...) otLogDebg(OT_LOG_REGION_API, __VA_ARGS__)
-#else
-#define otLogCritApi(...)
-#define otLogWarnApi(...)
-#define otLogNoteApi(...)
-#define otLogInfoApi(...)
-#define otLogDebgApi(...)
-#endif
-
-/**
- * @def otLogCritMeshCoP
- *
- * This function generates a log with level critical for the MeshCoP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- *
- */
-
-/**
- * @def otLogWarnMeshCoP
- *
- * This function generates a log with level warning for the MeshCoP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteMeshCoP
- *
- * This function generates a log with level note for the MeshCoP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoMeshCoP
- *
- * This function generates a log with level info for the MeshCoP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgMeshCoP
- *
- * This function generates a log with level debug for the MeshCoP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MESHCOP
-#define otLogCritMeshCoP(...) otLogCrit(OT_LOG_REGION_MESH_COP, __VA_ARGS__)
-#define otLogWarnMeshCoP(...) otLogWarn(OT_LOG_REGION_MESH_COP, __VA_ARGS__)
-#define otLogNoteMeshCoP(...) otLogNote(OT_LOG_REGION_MESH_COP, __VA_ARGS__)
-#define otLogInfoMeshCoP(...) otLogInfo(OT_LOG_REGION_MESH_COP, __VA_ARGS__)
-#define otLogDebgMeshCoP(...) otLogDebg(OT_LOG_REGION_MESH_COP, __VA_ARGS__)
-#else
-#define otLogCritMeshCoP(...)
-#define otLogWarnMeshCoP(...)
-#define otLogNoteMeshCoP(...)
-#define otLogInfoMeshCoP(...)
-#define otLogDebgMeshCoP(...)
-#endif
-
-#define otLogCritMbedTls(...) otLogCritMeshCoP(__VA_ARGS__)
-#define otLogWarnMbedTls(...) otLogWarnMeshCoP(__VA_ARGS__)
-#define otLogNoteMbedTls(...) otLogNoteMeshCoP(__VA_ARGS__)
-#define otLogInfoMbedTls(...) otLogInfoMeshCoP(__VA_ARGS__)
-#define otLogDebgMbedTls(...) otLogDebgMeshCoP(__VA_ARGS__)
-
-/**
- * @def otLogCritBr
- *
- * This function generates a log with level critical for the BR region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnBr
- *
- * This function generates a log with level warning for the BR region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteBr
- *
- * This function generates a log with level note for the BR region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoBr
- *
- * This function generates a log with level info for the BR region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgBr
- *
- * This function generates a log with level debug for the BR region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_BR
-#define otLogCritBr(...) otLogCrit(OT_LOG_REGION_BR, __VA_ARGS__)
-#define otLogWarnBr(...) otLogWarn(OT_LOG_REGION_BR, __VA_ARGS__)
-#define otLogNoteBr(...) otLogNote(OT_LOG_REGION_BR, __VA_ARGS__)
-#define otLogInfoBr(...) otLogInfo(OT_LOG_REGION_BR, __VA_ARGS__)
-#define otLogDebgBr(...) otLogDebg(OT_LOG_REGION_BR, __VA_ARGS__)
-#else
-#define otLogCritBr(...)
-#define otLogWarnBr(...)
-#define otLogNoteBr(...)
-#define otLogInfoBr(...)
-#define otLogDebgBr(...)
-#endif
-
-/**
- * @def otLogCritMle
- *
- * This function generates a log with level critical for the MLE region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnMle
- *
- * This function generates a log with level warning for the MLE region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteMle
- *
- * This function generates a log with level note for the MLE region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoMle
- *
- * This function generates a log with level info for the MLE region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgMle
- *
- * This function generates a log with level debug for the MLE region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MLE
-#define otLogCritMle(...) otLogCrit(OT_LOG_REGION_MLE, __VA_ARGS__)
-#define otLogWarnMle(...) otLogWarn(OT_LOG_REGION_MLE, __VA_ARGS__)
-#define otLogNoteMle(...) otLogNote(OT_LOG_REGION_MLE, __VA_ARGS__)
-#define otLogInfoMle(...) otLogInfo(OT_LOG_REGION_MLE, __VA_ARGS__)
-#define otLogDebgMle(...) otLogDebg(OT_LOG_REGION_MLE, __VA_ARGS__)
-#else
-#define otLogCritMle(...)
-#define otLogWarnMle(...)
-#define otLogNoteMle(...)
-#define otLogInfoMle(...)
-#define otLogDebgMle(...)
-#endif
-
-/**
- * @def otLogCritArp
- *
- * This function generates a log with level critical for the EID-to-RLOC mapping region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnArp
- *
- * This function generates a log with level warning for the EID-to-RLOC mapping region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteArp
- *
- * This function generates a log with level note for the EID-to-RLOC mapping region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoArp
- *
- * This function generates a log with level info for the EID-to-RLOC mapping region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgArp
- *
- * This function generates a log with level debug for the EID-to-RLOC mapping region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_ARP
-#define otLogCritArp(...) otLogCrit(OT_LOG_REGION_ARP, __VA_ARGS__)
-#define otLogWarnArp(...) otLogWarn(OT_LOG_REGION_ARP, __VA_ARGS__)
-#define otLogNoteArp(...) otLogNote(OT_LOG_REGION_ARP, __VA_ARGS__)
-#define otLogInfoArp(...) otLogInfo(OT_LOG_REGION_ARP, __VA_ARGS__)
-#define otLogDebgArp(...) otLogDebg(OT_LOG_REGION_ARP, __VA_ARGS__)
-#else
-#define otLogCritArp(...)
-#define otLogWarnArp(...)
-#define otLogNoteArp(...)
-#define otLogInfoArp(...)
-#define otLogDebgArp(...)
-#endif
-
-/**
- * @def otLogCritBbr
- *
- * This function generates a log with level critical for the Backbone Router (BBR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnBbr
- *
- * This function generates a log with level warning for the Backbone Router (BBR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteBbr
- *
- * This function generates a log with level note for the Backbone Router (BBR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoBbr
- *
- * This function generates a log with level info for the Backbone Router (BBR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgBbr
- *
- * This function generates a log with level debug for the Backbone Router (BBR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_BBR
-#define otLogCritBbr(...) otLogCrit(OT_LOG_REGION_BBR, __VA_ARGS__)
-#define otLogWarnBbr(...) otLogWarn(OT_LOG_REGION_BBR, __VA_ARGS__)
-#define otLogNoteBbr(...) otLogNote(OT_LOG_REGION_BBR, __VA_ARGS__)
-#define otLogInfoBbr(...) otLogInfo(OT_LOG_REGION_BBR, __VA_ARGS__)
-#define otLogDebgBbr(...) otLogDebg(OT_LOG_REGION_BBR, __VA_ARGS__)
-#else
-#define otLogCritBbr(...)
-#define otLogWarnBbr(...)
-#define otLogNoteBbr(...)
-#define otLogInfoBbr(...)
-#define otLogDebgBbr(...)
-#endif
-
-/**
- * @def otLogCritMlr
- *
- * This function generates a log with level critical for the Multicast Listener Registration (MLR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnMlr
- *
- * This function generates a log with level warning for the Multicast Listener Registration (MLR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteMlr
- *
- * This function generates a log with level note for the Multicast Listener Registration (MLR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoMlr
- *
- * This function generates a log with level info for the Multicast Listener Registration (MLR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgMlr
- *
- * This function generates a log with level debug for the Multicast Listener Registration (MLR) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MLR
-#define otLogCritMlr(...) otLogCrit(OT_LOG_REGION_MLR, __VA_ARGS__)
-#define otLogWarnMlr(...) otLogWarn(OT_LOG_REGION_MLR, __VA_ARGS__)
-#define otLogNoteMlr(...) otLogNote(OT_LOG_REGION_MLR, __VA_ARGS__)
-#define otLogInfoMlr(...) otLogInfo(OT_LOG_REGION_MLR, __VA_ARGS__)
-#define otLogDebgMlr(...) otLogDebg(OT_LOG_REGION_MLR, __VA_ARGS__)
-#else
-#define otLogCritMlr(...)
-#define otLogWarnMlr(...)
-#define otLogNoteMlr(...)
-#define otLogInfoMlr(...)
-#define otLogDebgMlr(...)
-#endif
-
-/**
- * @def otLogCritNetData
- *
- * This function generates a log with level critical for the Network Data region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnNetData
- *
- * This function generates a log with level warning for the Network Data region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteNetData
- *
- * This function generates a log with level note for the Network Data region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoNetData
- *
- * This function generates a log with level info for the Network Data region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgNetData
- *
- * This function generates a log with level debug for the Network Data region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_NETDATA
-#define otLogCritNetData(...) otLogCrit(OT_LOG_REGION_NET_DATA, __VA_ARGS__)
-#define otLogWarnNetData(...) otLogWarn(OT_LOG_REGION_NET_DATA, __VA_ARGS__)
-#define otLogNoteNetData(...) otLogNote(OT_LOG_REGION_NET_DATA, __VA_ARGS__)
-#define otLogInfoNetData(...) otLogInfo(OT_LOG_REGION_NET_DATA, __VA_ARGS__)
-#define otLogDebgNetData(...) otLogDebg(OT_LOG_REGION_NET_DATA, __VA_ARGS__)
-#else
-#define otLogCritNetData(...)
-#define otLogWarnNetData(...)
-#define otLogNoteNetData(...)
-#define otLogInfoNetData(...)
-#define otLogDebgNetData(...)
-#endif
-
-/**
- * @def otLogCritIcmp
- *
- * This function generates a log with level critical for the ICMPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnIcmp
- *
- * This function generates a log with level warning for the ICMPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteIcmp
- *
- * This function generates a log with level note for the ICMPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoIcmp
- *
- * This function generates a log with level info for the ICMPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgIcmp
- *
- * This function generates a log with level debug for the ICMPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_ICMP
-#define otLogCritIcmp(...) otLogCrit(OT_LOG_REGION_ICMP, __VA_ARGS__)
-#define otLogWarnIcmp(...) otLogWarn(OT_LOG_REGION_ICMP, __VA_ARGS__)
-#define otLogNoteIcmp(...) otLogNote(OT_LOG_REGION_ICMP, __VA_ARGS__)
-#define otLogInfoIcmp(...) otLogInfo(OT_LOG_REGION_ICMP, __VA_ARGS__)
-#define otLogDebgIcmp(...) otLogDebg(OT_LOG_REGION_ICMP, __VA_ARGS__)
-#else
-#define otLogCritIcmp(...)
-#define otLogWarnIcmp(...)
-#define otLogNoteIcmp(...)
-#define otLogInfoIcmp(...)
-#define otLogDebgIcmp(...)
-#endif
-
-/**
- * @def otLogCritIp6
- *
- * This function generates a log with level critical for the IPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnIp6
- *
- * This function generates a log with level warning for the IPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteIp6
- *
- * This function generates a log with level note for the IPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoIp6
- *
- * This function generates a log with level info for the IPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgIp6
- *
- * This function generates a log with level debug for the IPv6 region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_IP6
-#define otLogCritIp6(...) otLogCrit(OT_LOG_REGION_IP6, __VA_ARGS__)
-#define otLogWarnIp6(...) otLogWarn(OT_LOG_REGION_IP6, __VA_ARGS__)
-#define otLogNoteIp6(...) otLogNote(OT_LOG_REGION_IP6, __VA_ARGS__)
-#define otLogInfoIp6(...) otLogInfo(OT_LOG_REGION_IP6, __VA_ARGS__)
-#define otLogDebgIp6(...) otLogDebg(OT_LOG_REGION_IP6, __VA_ARGS__)
-#else
-#define otLogCritIp6(...)
-#define otLogWarnIp6(...)
-#define otLogNoteIp6(...)
-#define otLogInfoIp6(...)
-#define otLogDebgIp6(...)
-#endif
-
-/**
- * @def otLogCritTcp
- *
- * This function generates a log with level critical for the TCP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnTcp
- *
- * This function generates a log with level warning for the TCP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteTcp
- *
- * This function generates a log with level note for the TCP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoTcp
- *
- * This function generates a log with level info for the TCP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgTcp
- *
- * This function generates a log with level debug for the TCP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_TCP
-#define otLogCritTcp(...) otLogCrit(OT_LOG_REGION_TCP, __VA_ARGS__)
-#define otLogWarnTcp(...) otLogWarn(OT_LOG_REGION_TCP, __VA_ARGS__)
-#define otLogNoteTcp(...) otLogNote(OT_LOG_REGION_TCP, __VA_ARGS__)
-#define otLogInfoTcp(...) otLogInfo(OT_LOG_REGION_TCP, __VA_ARGS__)
-#define otLogDebgTcp(...) otLogDebg(OT_LOG_REGION_TCP, __VA_ARGS__)
-#else
-#define otLogCritTcp(...)
-#define otLogWarnTcp(...)
-#define otLogNoteTcp(...)
-#define otLogInfoTcp(...)
-#define otLogDebgTcp(...)
-#endif
-
-/**
- * @def otLogCritMac
- *
- * This function generates a log with level critical for the MAC region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnMac
- *
- * This function generates a log with level warning for the MAC region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteMac
- *
- * This function generates a log with level note for the MAC region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoMac
- *
- * This function generates a log with level info for the MAC region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgMac
- *
- * This function generates a log with level debug for the MAC region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogMac
- *
- * This function generates a log with a given log level for the MAC region.
- *
- * @param[in]  aLogLevel  A log level.
- * @param[in]  aFormat    A pointer to the format string.
- * @param[in]  ...        Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MAC
-#define otLogCritMac(...) otLogCrit(OT_LOG_REGION_MAC, __VA_ARGS__)
-#define otLogWarnMac(...) otLogWarn(OT_LOG_REGION_MAC, __VA_ARGS__)
-#define otLogNoteMac(...) otLogNote(OT_LOG_REGION_MAC, __VA_ARGS__)
-#define otLogInfoMac(...) otLogInfo(OT_LOG_REGION_MAC, __VA_ARGS__)
-#define otLogDebgMac(...) otLogDebg(OT_LOG_REGION_MAC, __VA_ARGS__)
-void otLogMac(otLogLevel aLogLevel, const char *aFormat, ...);
-#else
-#define otLogCritMac(...)
-#define otLogWarnMac(...)
-#define otLogNoteMac(...)
-#define otLogInfoMac(...)
-#define otLogDebgMac(...)
-#define otLogMac(aLogLevel, ...)
-#endif
-
-/**
- * @def otLogCritCore
- *
- * This function generates a log with level critical for the Core region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnCore
- *
- * This function generates a log with level warning for the Core region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteCore
- *
- * This function generates a log with level note for the Core region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoCore
- *
- * This function generates a log with level info for the Core region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgCore
- *
- * This function generates a log with level debug for the Core region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_CORE
-#define otLogCritCore(...) otLogCrit(OT_LOG_REGION_CORE, __VA_ARGS__)
-#define otLogWarnCore(...) otLogWarn(OT_LOG_REGION_CORE, __VA_ARGS__)
-#define otLogNoteCore(...) otLogNote(OT_LOG_REGION_CORE, __VA_ARGS__)
-#define otLogInfoCore(...) otLogInfo(OT_LOG_REGION_CORE, __VA_ARGS__)
-#define otLogDebgCore(...) otLogDebg(OT_LOG_REGION_CORE, __VA_ARGS__)
-#else
-#define otLogCritCore(...)
-#define otLogWarnCore(...)
-#define otLogInfoCore(...)
-#define otLogDebgCore(...)
-#endif
-
-/**
- * @def otLogCritMem
- *
- * This function generates a log with level critical for the memory region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnMem
- *
- * This function generates a log with level warning for the memory region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteMem
- *
- * This function generates a log with level note for the memory region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoMem
- *
- * This function generates a log with level info for the memory region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgMem
- *
- * This function generates a log with level debug for the memory region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MEM
-#define otLogCritMem(...) otLogCrit(OT_LOG_REGION_MEM, __VA_ARGS__)
-#define otLogWarnMem(...) otLogWarn(OT_LOG_REGION_MEM, __VA_ARGS__)
-#define otLogNoteMem(...) otLogNote(OT_LOG_REGION_MEM, __VA_ARGS__)
-#define otLogInfoMem(...) otLogInfo(OT_LOG_REGION_MEM, __VA_ARGS__)
-#define otLogDebgMem(...) otLogDebg(OT_LOG_REGION_MEM, __VA_ARGS__)
-#else
-#define otLogCritMem(...)
-#define otLogWarnMem(...)
-#define otLogNoteMem(...)
-#define otLogInfoMem(...)
-#define otLogDebgMem(...)
-#endif
-
-/**
- * @def otLogCritUtil
- *
- * This function generates a log with level critical for the Util region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnUtil
- *
- * This function generates a log with level warning for the Util region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteUtil
- *
- * This function generates a log with level note for the Util region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoUtil
- *
- * This function generates a log with level info for the Util region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgUtil
- *
- * This function generates a log with level debug for the Util region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_UTIL
-#define otLogCritUtil(...) otLogCrit(OT_LOG_REGION_UTIL, __VA_ARGS__)
-#define otLogWarnUtil(...) otLogWarn(OT_LOG_REGION_UTIL, __VA_ARGS__)
-#define otLogNoteUtil(...) otLogNote(OT_LOG_REGION_UTIL, __VA_ARGS__)
-#define otLogInfoUtil(...) otLogInfo(OT_LOG_REGION_UTIL, __VA_ARGS__)
-#define otLogDebgUtil(...) otLogDebg(OT_LOG_REGION_UTIL, __VA_ARGS__)
-#else
-#define otLogCritUtil(...)
-#define otLogWarnUtil(...)
-#define otLogNoteUtil(...)
-#define otLogInfoUtil(...)
-#define otLogDebgUtil(...)
-#endif
-
-/**
- * @def otLogCritNetDiag
- *
- * This function generates a log with level critical for the NETDIAG region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnNetDiag
- *
- * This function generates a log with level warning for the NETDIAG region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteNetDiag
- *
- * This function generates a log with level note for the NETDIAG region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoNetDiag
- *
- * This function generates a log with level info for the NETDIAG region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgNetDiag
- *
- * This function generates a log with level debug for the NETDIAG region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_NETDIAG
-#define otLogCritNetDiag(...) otLogCrit(OT_LOG_REGION_NET_DIAG, __VA_ARGS__)
-#define otLogWarnNetDiag(...) otLogWarn(OT_LOG_REGION_NET_DIAG, __VA_ARGS__)
-#define otLogNoteNetDiag(...) otLogNote(OT_LOG_REGION_NET_DIAG, __VA_ARGS__)
-#define otLogInfoNetDiag(...) otLogInfo(OT_LOG_REGION_NET_DIAG, __VA_ARGS__)
-#define otLogDebgNetDiag(...) otLogDebg(OT_LOG_REGION_NET_DIAG, __VA_ARGS__)
-#else
-#define otLogCritNetDiag(...)
-#define otLogWarnNetDiag(...)
-#define otLogNoteNetDiag(...)
-#define otLogInfoNetDiag(...)
-#define otLogDebgNetDiag(...)
-#endif
-
-/**
- * @def otLogCert
- *
- * This function generates a log with level none for the certification test.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- *
- */
-#if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
-#define otLogCertMeshCoP(...)
-#else
-void otLogCertMeshCoP(const char *aFormat, ...);
-#endif
-
-/**
- * @def otLogCritCli
- *
- * This function generates a log with level critical for the CLI region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnCli
- *
- * This function generates a log with level warning for the CLI region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteCli
- *
- * This function generates a log with level note for the CLI region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoCli
- *
- * This function generates a log with level info for the CLI region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgCli
- *
- * This function generates a log with level debug for the CLI region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_CLI
-
-#define otLogCritCli(...) otLogCrit(OT_LOG_REGION_CLI, __VA_ARGS__)
-#define otLogWarnCli(...) otLogWarn(OT_LOG_REGION_CLI, __VA_ARGS__)
-#define otLogNoteCli(...) otLogNote(OT_LOG_REGION_CLI, __VA_ARGS__)
-#define otLogInfoCli(...) otLogInfo(OT_LOG_REGION_CLI, __VA_ARGS__)
-#define otLogDebgCli(...) otLogDebg(OT_LOG_REGION_CLI, __VA_ARGS__)
-#else
-#define otLogCritCli(...)
-#define otLogWarnCli(...)
-#define otLogNoteCli(...)
-#define otLogInfoCli(...)
-#define otLogDebgCli(...)
-#endif
-
-/**
- * @def otLogCritCoap
- *
- * This function generates a log with level critical for the CoAP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnCoap
- *
- * This function generates a log with level warning for the CoAP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteCoap
- *
- * This function generates a log with level note for the CoAP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoCoap
- *
- * This function generates a log with level info for the CoAP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgCoap
- *
- * This function generates a log with level debug for the CoAP region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_COAP
-#define otLogCritCoap(...) otLogCrit(OT_LOG_REGION_COAP, __VA_ARGS__)
-#define otLogWarnCoap(...) otLogWarn(OT_LOG_REGION_COAP, __VA_ARGS__)
-#define otLogNoteCoap(...) otLogNote(OT_LOG_REGION_COAP, __VA_ARGS__)
-#define otLogInfoCoap(...) otLogInfo(OT_LOG_REGION_COAP, __VA_ARGS__)
-#define otLogDebgCoap(...) otLogDebg(OT_LOG_REGION_COAP, __VA_ARGS__)
-#else
-#define otLogCritCoap(...)
-#define otLogWarnCoap(...)
-#define otLogNoteCoap(...)
-#define otLogInfoCoap(...)
-#define otLogDebgCoap(...)
-#endif
-
-/**
- * @def otLogCritDua
- *
- * This function generates a log with level critical for the Domain Unicast Address region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnDua
- *
- * This function generates a log with level warning for the Domain Unicast Address region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteDua
- *
- * This function generates a log with level note for the Domain Unicast Address region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoDua
- *
- * This function generates a log with level info for the Domain Unicast Address region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgDua
- *
- * This function generates a log with level debug for the Domain Unicast Address region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_DUA
-#define otLogCritDua(...) otLogCrit(OT_LOG_REGION_DUA, __VA_ARGS__)
-#define otLogWarnDua(...) otLogWarn(OT_LOG_REGION_DUA, __VA_ARGS__)
-#define otLogNoteDua(...) otLogNote(OT_LOG_REGION_DUA, __VA_ARGS__)
-#define otLogInfoDua(...) otLogInfo(OT_LOG_REGION_DUA, __VA_ARGS__)
-#define otLogDebgDua(...) otLogDebg(OT_LOG_REGION_DUA, __VA_ARGS__)
-#else
-#define otLogCritDua(...)
-#define otLogWarnDua(...)
-#define otLogNoteDua(...)
-#define otLogInfoDua(...)
-#define otLogDebgDua(...)
-#endif
-
-/**
- * @def otLogCritSrp
- *
- * This function generates a log with level critical for the Service Registration Protocol (SRP) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnSrp
- *
- * This function generates a log with level warning for the Service Registration Protocol (SRP) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteSrp
- *
- * This function generates a log with level note for the Service Registration Protocol (SRP) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoSrp
- *
- * This function generates a log with level info for the Service Registration Protocol (SRP) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgSrp
- *
- * This function generates a log with level debug for the Service Registration Protocol (SRP) region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_SRP
-#define otLogCritSrp(...) otLogCrit(OT_LOG_REGION_SRP, __VA_ARGS__)
-#define otLogWarnSrp(...) otLogWarn(OT_LOG_REGION_SRP, __VA_ARGS__)
-#define otLogNoteSrp(...) otLogNote(OT_LOG_REGION_SRP, __VA_ARGS__)
-#define otLogInfoSrp(...) otLogInfo(OT_LOG_REGION_SRP, __VA_ARGS__)
-#define otLogDebgSrp(...) otLogDebg(OT_LOG_REGION_SRP, __VA_ARGS__)
-#else
-#define otLogCritSrp(...)
-#define otLogWarnSrp(...)
-#define otLogNoteSrp(...)
-#define otLogInfoSrp(...)
-#define otLogDebgSrp(...)
-#endif
-
-/**
- * @def otLogCritDns
- *
- * This function generates a log with level critical for the DNS region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnDns
- *
- * This function generates a log with level warning for the DNS region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNoteDns
- *
- * This function generates a log with level note for the DNS region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoDns
- *
- * This function generates a log with level info for the DNS region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgDns
- *
- * This function generates a log with level debug for the DNS region.
- *
- * @param[in]  ...  Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_DNS
-#define otLogCritDns(...) otLogCrit(OT_LOG_REGION_DNS, __VA_ARGS__)
-#define otLogWarnDns(...) otLogWarn(OT_LOG_REGION_DNS, __VA_ARGS__)
-#define otLogNoteDns(...) otLogNote(OT_LOG_REGION_DNS, __VA_ARGS__)
-#define otLogInfoDns(...) otLogInfo(OT_LOG_REGION_DNS, __VA_ARGS__)
-#define otLogDebgDns(...) otLogDebg(OT_LOG_REGION_DNS, __VA_ARGS__)
-#else
-#define otLogCritDns(...)
-#define otLogWarnDns(...)
-#define otLogNoteDns(...)
-#define otLogInfoDns(...)
-#define otLogDebgDns(...)
-#endif
-
-/**
- * @def otLogCritPlat
- *
- * This function generates a log with level critical for the Platform region.
- *
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogWarnPlat
- *
- * This function generates a log with level warning for the Platform region.
- *
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogNotePlat
- *
- * This function generates a log with level note for the Platform region.
- *
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogInfoPlat
- *
- * This function generates a log with level info for the Platform region.
- *
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-
-/**
- * @def otLogDebgPlat
- *
- * This function generates a log with level debug for the Platform region.
- *
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_PLATFORM
-#define otLogCritPlat(...) otLogCrit(OT_LOG_REGION_PLATFORM, __VA_ARGS__)
-#define otLogWarnPlat(...) otLogWarn(OT_LOG_REGION_PLATFORM, __VA_ARGS__)
-#define otLogNotePlat(...) otLogNote(OT_LOG_REGION_PLATFORM, __VA_ARGS__)
-#define otLogInfoPlat(...) otLogInfo(OT_LOG_REGION_PLATFORM, __VA_ARGS__)
-#define otLogDebgPlat(...) otLogDebg(OT_LOG_REGION_PLATFORM, __VA_ARGS__)
-#else
-#define otLogCritPlat(...)
-#define otLogWarnPlat(...)
-#define otLogNotePlat(...)
-#define otLogInfoPlat(...)
-#define otLogDebgPlat(...)
-#endif
-
-/**
- * @def otLogOtns
- *
- * This function generates a log with level none for the Core region,
- * and is specifically for OTNS visualization use.
- *
- * @param[in]  ...       Arguments for the format specification.
- *
- */
-#if OPENTHREAD_CONFIG_OTNS_ENABLE
-void otLogOtns(const char *aFormat, ...);
-#endif
-
-/**
- * @def otDumpCrit
- *
- * This function generates a memory dump with log level critical.
- *
- * @param[in]  aRegion      The log region.
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_CRIT
-#define otDumpCrit(aRegion, aId, aBuf, aLength) otDump(OT_LOG_LEVEL_CRIT, aRegion, aId, aBuf, aLength)
-#else
-#define otDumpCrit(aRegion, aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpWarn
- *
- * This function generates a memory dump with log level warning.
- *
- * @param[in]  aRegion      The log region.
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN
-#define otDumpWarn(aRegion, aId, aBuf, aLength) otDump(OT_LOG_LEVEL_WARN, aRegion, aId, aBuf, aLength)
-#else
-#define otDumpWarn(aRegion, aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpNote
- *
- * This function generates a memory dump with log level note.
- *
- * @param[in]  aRegion      The log region.
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_NOTE
-#define otDumpNote(aRegion, aId, aBuf, aLength) otDump(OT_LOG_LEVEL_NOTE, aRegion, aId, aBuf, aLength)
-#else
-#define otDumpNote(aRegion, aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpInfo
- *
- * This function generates a memory dump with log level info.
- *
- * @param[in]  aRegion      The log region.
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO
-#define otDumpInfo(aRegion, aId, aBuf, aLength) otDump(OT_LOG_LEVEL_INFO, aRegion, aId, aBuf, aLength)
-#else
-#define otDumpInfo(aRegion, aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpDebg
- *
- * This function generates a memory dump with log level debug.
- *
- * @param[in]  aRegion      The log region.
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG
-#define otDumpDebg(aRegion, aId, aBuf, aLength) otDump(OT_LOG_LEVEL_DEBG, aRegion, aId, aBuf, aLength)
-#else
-#define otDumpDebg(aRegion, aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritNetData
- *
- * This function generates a memory dump with log level debug and region Network Data.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnNetData
- *
- * This function generates a memory dump with log level warning and region Network Data.
- *
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteNetData
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoNetData
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgNetData
- *
- * This function generates a memory dump with log level debug and region Network Data.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_NETDATA
-#define otDumpCritNetData(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_NET_DATA, aId, aBuf, aLength)
-#define otDumpWarnNetData(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_NET_DATA, aId, aBuf, aLength)
-#define otDumpNoteNetData(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_NET_DATA, aId, aBuf, aLength)
-#define otDumpInfoNetData(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_NET_DATA, aId, aBuf, aLength)
-#define otDumpDebgNetData(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_NET_DATA, aId, aBuf, aLength)
-#else
-#define otDumpCritNetData(aId, aBuf, aLength)
-#define otDumpWarnNetData(aId, aBuf, aLength)
-#define otDumpNoteNetData(aId, aBuf, aLength)
-#define otDumpInfoNetData(aId, aBuf, aLength)
-#define otDumpDebgNetData(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritMle
- *
- * This function generates a memory dump with log level debug and region MLE.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnMle
- *
- * This function generates a memory dump with log level warning and region MLE.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteMle
- *
- * This function generates a memory dump with log level note and region MLE.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoMle
- *
- * This function generates a memory dump with log level info and region MLE.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgMle
- *
- * This function generates a memory dump with log level debug and region MLE.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MLE
-#define otDumpCritMle(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_MLE, aId, aBuf, aLength)
-#define otDumpWarnMle(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_MLE, aId, aBuf, aLength)
-#define otDumpNoteMle(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_MLE, aId, aBuf, aLength)
-#define otDumpInfoMle(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_MLE, aId, aBuf, aLength)
-#define otDumpDebgMle(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_MLE, aId, aBuf, aLength)
-#else
-#define otDumpCritMle(aId, aBuf, aLength)
-#define otDumpWarnMle(aId, aBuf, aLength)
-#define otDumpNoteMle(aId, aBuf, aLength)
-#define otDumpInfoMle(aId, aBuf, aLength)
-#define otDumpDebgMle(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritArp
- *
- * This function generates a memory dump with log level debug and region EID-to-RLOC mapping.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnArp
- *
- * This function generates a memory dump with log level warning and region EID-to-RLOC mapping.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteArp
- *
- * This function generates a memory dump with log level note and region EID-to-RLOC mapping.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoArp
- *
- * This function generates a memory dump with log level info and region EID-to-RLOC mapping.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgArp
- *
- * This function generates a memory dump with log level debug and region EID-to-RLOC mapping.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_ARP
-#define otDumpCritArp(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_ARP, aId, aBuf, aLength)
-#define otDumpWarnArp(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_ARP, aId, aBuf, aLength)
-#define otDumpNoteArp(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_ARP, aId, aBuf, aLength)
-#define otDumpInfoArp(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_ARP, aId, aBuf, aLength)
-#define otDumpDebgArp(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_ARP, aId, aBuf, aLength)
-#else
-#define otDumpCritArp(aId, aBuf, aLength)
-#define otDumpWarnArp(aId, aBuf, aLength)
-#define otDumpNoteArp(aId, aBuf, aLength)
-#define otDumpInfoArp(aId, aBuf, aLength)
-#define otDumpDebgArp(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritBbr
- *
- * This function generates a memory dump with log level critical and region Backbone Router (BBR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnBbr
- *
- * This function generates a memory dump with log level warning and region Backbone Router (BBR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteBbr
- *
- * This function generates a memory dump with log level note and region Backbone Router (BBR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoBbr
- *
- * This function generates a memory dump with log level info and region Backbone Router (BBR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgBbr
- *
- * This function generates a memory dump with log level debug and region Backbone Router (BBR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_BBR
-#define otDumpCritBbr(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_BBR, aId, aBuf, aLength)
-#define otDumpWarnBbr(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_BBR, aId, aBuf, aLength)
-#define otDumpNoteBbr(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_BBR, aId, aBuf, aLength)
-#define otDumpInfoBbr(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_BBR, aId, aBuf, aLength)
-#define otDumpDebgBbr(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_BBR, aId, aBuf, aLength)
-#else
-#define otDumpCritBbr(aId, aBuf, aLength)
-#define otDumpWarnBbr(aId, aBuf, aLength)
-#define otDumpNoteBbr(aId, aBuf, aLength)
-#define otDumpInfoBbr(aId, aBuf, aLength)
-#define otDumpDebgBbr(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritMlr
- *
- * This function generates a memory dump with log level critical and region Multicast Listener Registration (MLR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnMlr
- *
- * This function generates a memory dump with log level warning and region Multicast Listener Registration (MLR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteMlr
- *
- * This function generates a memory dump with log level note and region Multicast Listener Registration (MLR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoMlr
- *
- * This function generates a memory dump with log level info and region Multicast Listener Registration (MLR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgMlr
- *
- * This function generates a memory dump with log level debug and region Multicast Listener Registration (MLR).
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_MLR
-#define otDumpCritMlr(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_MLR, aId, aBuf, aLength)
-#define otDumpWarnMlr(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_MLR, aId, aBuf, aLength)
-#define otDumpNoteMlr(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_MLR, aId, aBuf, aLength)
-#define otDumpInfoMlr(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_MLR, aId, aBuf, aLength)
-#define otDumpDebgMlr(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_MLR, aId, aBuf, aLength)
-#else
-#define otDumpCritMlr(aId, aBuf, aLength)
-#define otDumpWarnMlr(aId, aBuf, aLength)
-#define otDumpNoteMlr(aId, aBuf, aLength)
-#define otDumpInfoMlr(aId, aBuf, aLength)
-#define otDumpDebgMlr(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritIcmp
- *
- * This function generates a memory dump with log level debug and region ICMPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnIcmp
- *
- * This function generates a memory dump with log level warning and region ICMPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteIcmp
- *
- * This function generates a memory dump with log level note and region ICMPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoIcmp
- *
- * This function generates a memory dump with log level info and region ICMPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgIcmp
- *
- * This function generates a memory dump with log level debug and region ICMPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_ICMP
-#define otDumpCritIcmp(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_ICMP, aId, aBuf, aLength)
-#define otDumpWarnIcmp(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_ICMP, aId, aBuf, aLength)
-#define otDumpNoteIcmp(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_ICMP, aId, aBuf, aLength)
-#define otDumpInfoIcmp(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_ICMP, aId, aBuf, aLength)
-#define otDumpDebgIcmp(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_ICMP, aId, aBuf, aLength)
-#else
-#define otDumpCritIcmp(aId, aBuf, aLength)
-#define otDumpWarnIcmp(aId, aBuf, aLength)
-#define otDumpNoteIcmp(aId, aBuf, aLength)
-#define otDumpInfoIcmp(aId, aBuf, aLength)
-#define otDumpDebgIcmp(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritIp6
- *
- * This function generates a memory dump with log level debug and region IPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWarnIp6
- *
- * This function generates a memory dump with log level warning and region IPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNoteIp6
- *
- * This function generates a memory dump with log level note and region IPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInfoIp6
- *
- * This function generates a memory dump with log level info and region IPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpDebgIp6
- *
- * This function generates a memory dump with log level debug and region IPv6.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-#if OPENTHREAD_CONFIG_LOG_IP6
-#define otDumpCritIp6(aId, aBuf, aLength) otDumpCrit(OT_LOG_REGION_IP6, aId, aBuf, aLength)
-#define otDumpWarnIp6(aId, aBuf, aLength) otDumpWarn(OT_LOG_REGION_IP6, aId, aBuf, aLength)
-#define otDumpNoteIp6(aId, aBuf, aLength) otDumpNote(OT_LOG_REGION_IP6, aId, aBuf, aLength)
-#define otDumpInfoIp6(aId, aBuf, aLength) otDumpInfo(OT_LOG_REGION_IP6, aId, aBuf, aLength)
-#define otDumpDebgIp6(aId, aBuf, aLength) otDumpDebg(OT_LOG_REGION_IP6, aId, aBuf, aLength)
-#else
-#define otDumpCritIp6(aId, aBuf, aLength)
-#define otDumpWarnIp6(aId, aBuf, aLength)
-#define otDumpNoteIp6(aId, aBuf, aLength)
-#define otDumpInfoIp6(aId, aBuf, aLength)
-#define otDumpDebgIp6(aId, aBuf, aLength)
-#endif
-
-/**
- * @def otDumpCritTcp
- *
- * This function generates a memory dump with log level debug and region TCP.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpWartTcp
- *
- * This function generates a memory dump with log level warning and region TCP.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpNottTcp
- *
- * This function generates a memory dump with log level note and region TCP.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-
-/**
- * @def otDumpInftTcp
- *
- * This function generates a memory dump with log level info and region TCP.
- *
- * @param[in]  aId          A pointer to a NULL-terminated string that is printed before the bytes.
- * @param[in]  aBuf         A pointer to the buffer.
- * @param[in]  aLength      Number of bytes to print.
- *
- */
-