aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-10-14 12:08:40 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-10-14 12:08:40 +0000
commit5a1cccb7afbec08a2d0145fa243836638c2d194e (patch)
tree994a922ec4ce28a3c01fdc03b91888ef25f9da95
parentec2745badf10cb6560aeaefafe8f92fb508007f0 (diff)
parent6d7c9418cdf6ab0b2674cb553c3f1176204827f5 (diff)
downloadot-br-posix-5a1cccb7afbec08a2d0145fa243836638c2d194e.tar.gz
Snap for 10950907 from 6d7c9418cdf6ab0b2674cb553c3f1176204827f5 to mainline-media-swcodec-release
Change-Id: I62f6c26c2cca7a0f6c36162961f48277860a3480
-rw-r--r--.github/workflows/border_router.yml4
-rw-r--r--.github/workflows/build.yml14
-rw-r--r--.github/workflows/docker.yml10
-rw-r--r--.github/workflows/documentation.yml8
-rw-r--r--.github/workflows/macOS.yml4
-rw-r--r--.github/workflows/meshcop.yml2
-rw-r--r--.github/workflows/openwrt.yml2
-rw-r--r--.github/workflows/raspbian.yml2
-rw-r--r--CMakeLists.txt1
-rw-r--r--etc/cmake/options.cmake19
-rw-r--r--etc/docker/Dockerfile4
-rw-r--r--etc/openwrt/openthread-br/Makefile4
-rwxr-xr-xscript/_firewall10
-rw-r--r--script/_nat645
-rw-r--r--script/_otbr10
-rwxr-xr-xscript/bootstrap20
-rwxr-xr-xscript/test1
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/agent/CMakeLists.txt4
-rw-r--r--src/agent/application.cpp2
-rw-r--r--src/agent/application.hpp77
-rw-r--r--src/agent/vendor.hpp11
-rw-r--r--src/android/otdaemon_server.cpp15
-rw-r--r--src/android/otdaemon_server.hpp4
-rw-r--r--src/backbone_router/backbone_agent.hpp2
-rw-r--r--src/backbone_router/constants.hpp2
-rw-r--r--src/backbone_router/dua_routing_manager.hpp2
-rw-r--r--src/backbone_router/nd_proxy.hpp2
-rw-r--r--src/border_agent/border_agent.cpp32
-rw-r--r--src/border_agent/border_agent.hpp2
-rw-r--r--src/common/CMakeLists.txt4
-rw-r--r--src/common/api_strings.cpp55
-rw-r--r--src/common/api_strings.hpp52
-rw-r--r--src/common/byteswap.hpp2
-rw-r--r--src/common/callback.hpp26
-rw-r--r--src/common/code_utils.cpp40
-rw-r--r--src/common/code_utils.hpp14
-rw-r--r--src/common/dns_utils.hpp2
-rw-r--r--src/dbus/client/client_error.hpp2
-rw-r--r--src/dbus/client/thread_api_dbus.cpp16
-rw-r--r--src/dbus/client/thread_api_dbus.hpp40
-rw-r--r--src/dbus/common/constants.hpp10
-rw-r--r--src/dbus/common/dbus_message_dump.hpp2
-rw-r--r--src/dbus/common/dbus_message_helper.hpp6
-rw-r--r--src/dbus/common/dbus_message_helper_openthread.cpp15
-rw-r--r--src/dbus/common/dbus_resources.hpp2
-rw-r--r--src/dbus/common/error.hpp2
-rw-r--r--src/dbus/common/types.hpp2
-rw-r--r--src/dbus/server/CMakeLists.txt4
-rw-r--r--src/dbus/server/dbus_agent.hpp2
-rw-r--r--src/dbus/server/dbus_object.cpp4
-rw-r--r--src/dbus/server/dbus_object.hpp2
-rw-r--r--src/dbus/server/dbus_request.hpp7
-rw-r--r--src/dbus/server/dbus_thread_object.cpp155
-rw-r--r--src/dbus/server/dbus_thread_object.hpp7
-rw-r--r--src/dbus/server/error_helper.hpp2
-rw-r--r--src/dbus/server/introspect.xml35
-rw-r--r--src/mdns/mdns.cpp93
-rw-r--r--src/mdns/mdns.hpp159
-rw-r--r--src/mdns/mdns_avahi.cpp434
-rw-r--r--src/mdns/mdns_avahi.hpp209
-rw-r--r--src/mdns/mdns_mdnssd.cpp559
-rw-r--r--src/mdns/mdns_mdnssd.hpp153
-rw-r--r--src/ncp/CMakeLists.txt7
-rw-r--r--src/ncp/ncp_openthread.cpp17
-rw-r--r--src/ncp/ncp_openthread.hpp2
-rwxr-xr-xsrc/openwrt/otbr-agent.init.in5
-rw-r--r--src/openwrt/otbr-agent.uci-config.in3
-rw-r--r--src/openwrt/view/admin_thread/thread_view.htm2
-rw-r--r--src/proto/CMakeLists.txt15
-rw-r--r--src/proto/capabilities.proto14
-rw-r--r--src/proto/feature_flag.proto2
-rw-r--r--src/proto/thread_telemetry.proto489
-rw-r--r--src/rest/connection.hpp2
-rw-r--r--src/rest/json.cpp20
-rw-r--r--src/rest/json.hpp12
-rw-r--r--src/rest/openapi.yaml41
-rw-r--r--src/rest/parser.hpp2
-rw-r--r--src/rest/request.hpp2
-rw-r--r--src/rest/resource.cpp131
-rw-r--r--src/rest/resource.hpp5
-rw-r--r--src/rest/response.cpp2
-rw-r--r--src/rest/response.hpp2
-rw-r--r--src/rest/rest_web_server.hpp2
-rw-r--r--src/rest/types.hpp20
-rw-r--r--src/sdp_proxy/advertising_proxy.cpp54
-rw-r--r--src/sdp_proxy/advertising_proxy.hpp7
-rw-r--r--src/sdp_proxy/discovery_proxy.hpp2
-rw-r--r--src/trel_dnssd/trel_dnssd.cpp20
-rw-r--r--src/trel_dnssd/trel_dnssd.hpp8
-rw-r--r--src/utils/CMakeLists.txt6
-rw-r--r--src/utils/infra_link_selector.hpp2
-rw-r--r--src/utils/sha256.cpp105
-rw-r--r--src/utils/sha256.hpp118
-rw-r--r--src/utils/system_utils.hpp2
-rw-r--r--src/utils/thread_helper.cpp702
-rw-r--r--src/utils/thread_helper.hpp23
-rw-r--r--tests/dbus/CMakeLists.txt2
-rwxr-xr-xtests/dbus/test-client22
-rw-r--r--tests/dbus/test_dbus_client.cpp76
-rw-r--r--tests/mdns/main.cpp117
-rwxr-xr-xtests/rest/test-rest-server4
-rw-r--r--tests/rest/test_rest.py4
-rwxr-xr-xtests/scripts/bootstrap.sh17
-rwxr-xr-xtests/scripts/check-scan-build1
-rwxr-xr-xtests/scripts/openwrt2
-rw-r--r--third_party/mDNSResponder/0001-Fix-Linux-build.patch32
-rw-r--r--third_party/mDNSResponder/0002-Create-subroutine-for-cleaning-recent-interfaces.patch64
-rw-r--r--third_party/mDNSResponder/0003-Create-subroutine-for-tearing-down-an-interface.patch62
-rw-r--r--third_party/mDNSResponder/0004-Track-interface-socket-family.patch54
-rw-r--r--third_party/mDNSResponder/0005-Indicate-loopback-interface-to-mDNS-core.patch61
-rw-r--r--third_party/mDNSResponder/0006-Use-list-for-changed-interfaces.patch178
-rw-r--r--third_party/mDNSResponder/0007-Handle-noisy-netlink-sockets.patch255
-rw-r--r--third_party/mDNSResponder/0008-Mark-deleted-interfaces-as-being-changed.patch43
-rw-r--r--third_party/mDNSResponder/0009-Handle-errors-from-socket-calls.patch66
-rw-r--r--third_party/mDNSResponder/0010-Handle-interface-without-ifa_addr.patch41
-rw-r--r--third_party/openthread/CMakeLists.txt1
117 files changed, 4341 insertions, 1001 deletions
diff --git a/.github/workflows/border_router.yml b/.github/workflows/border_router.yml
index 9e754e1b..591853cc 100644
--- a/.github/workflows/border_router.yml
+++ b/.github/workflows/border_router.yml
@@ -99,7 +99,7 @@ jobs:
cert_scripts: ./tests/scripts/thread-cert/backbone/*.py
packet_verification: 1
- name: "Border Router TREL with FEATURE_FLAG (avahi)"
- otbr_options: "-DOT_DUA=ON -DOT_ECDSA=ON -DOT_MLR=ON -DOT_SERVICE=ON -DOT_SRP_SERVER=ON -DOTBR_COVERAGE=ON -DOTBR_DUA_ROUTING=ON -DOTBR_FEATURE_FLAGS=ON -DOTBR_TREL=ON -DOTBR_DNS_UPSTREAM_QUERY=ON"
+ otbr_options: "-DOT_DUA=ON -DOT_ECDSA=ON -DOT_MLR=ON -DOT_SERVICE=ON -DOT_SRP_SERVER=ON -DOTBR_COVERAGE=ON -DOTBR_DUA_ROUTING=ON -DOTBR_FEATURE_FLAGS=ON -DOTBR_TELEMETRY_DATA_API=ON -DOTBR_TREL=ON -DOTBR_DNS_UPSTREAM_QUERY=ON"
border_routing: 1
nat64: 0
otbr_mdns: "avahi"
@@ -122,7 +122,7 @@ jobs:
NAT64: ${{ matrix.nat64 }}
MAX_JOBS: 3
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Get Border Router Test ID
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 195c26e5..01e02e4d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -45,7 +45,7 @@ jobs:
pretty:
runs-on: ubuntu-22.04
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Bootstrap
run: BUILD_TARGET=pretty-check tests/scripts/bootstrap.sh
- name: Check
@@ -65,7 +65,7 @@ jobs:
OTBR_OPTIONS: "-DOTBR_SRP_ADVERTISING_PROXY=ON -DOTBR_BORDER_ROUTING=ON -DOTBR_NAT64=1 -DOTBR_SRP_SERVER_AUTO_ENABLE=OFF"
OTBR_COVERAGE: 1
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Bootstrap
@@ -88,7 +88,7 @@ jobs:
OTBR_OPTIONS: "-DOTBR_SRP_ADVERTISING_PROXY=ON -DOTBR_DNSSD_DISCOVERY_PROXY=ON"
OTBR_COVERAGE: 1
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Bootstrap
@@ -104,7 +104,7 @@ jobs:
BUILD_TARGET: script-check
OTBR_COVERAGE: 1
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Bootstrap
@@ -121,7 +121,7 @@ jobs:
CC: clang
CXX: clang++
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Bootstrap
@@ -134,7 +134,7 @@ jobs:
env:
BUILD_TARGET: package
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Bootstrap
@@ -151,7 +151,7 @@ jobs:
env:
BUILD_TARGET: check
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Bootstrap
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 9c794b11..bac8c4cb 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -53,7 +53,7 @@ jobs:
OTBR_COVERAGE: 1
VERBOSE: 1
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Bootstrap
@@ -106,7 +106,7 @@ jobs:
platforms: "linux/amd64,linux/arm/v7,linux/arm64"
push: no
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: true
@@ -130,12 +130,12 @@ jobs:
${TAGS} --file etc/docker/Dockerfile ." >> $GITHUB_OUTPUT
- name: Set up QEMU
- uses: docker/setup-qemu-action@v2
+ uses: docker/setup-qemu-action@v3
with:
platforms: all
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
+ uses: docker/setup-buildx-action@v3
- name: Docker Buildx (build)
run: |
@@ -143,7 +143,7 @@ jobs:
- name: Login to DockerHub
if: success() && github.repository == 'openthread/ot-br-posix' && github.event_name != 'pull_request' && matrix.push
- uses: docker/login-action@v2
+ uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
index 93065f4c..639ba7b4 100644
--- a/.github/workflows/documentation.yml
+++ b/.github/workflows/documentation.yml
@@ -40,14 +40,16 @@ concurrency:
jobs:
doxygen:
runs-on: ubuntu-latest
+ env:
+ BUILD_TARGET: check
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Bootstrap
run: |
- sudo apt-get update
- sudo apt-get install -y doxygen libdbus-1-dev libglib2.0-dev-bin xmlto
+ tests/scripts/bootstrap.sh
+ sudo apt-get install -y libglib2.0-dev-bin xmlto
- name: Generate
run: |
mkdir build-doc
diff --git a/.github/workflows/macOS.yml b/.github/workflows/macOS.yml
index 7e473ac6..46f98939 100644
--- a/.github/workflows/macOS.yml
+++ b/.github/workflows/macOS.yml
@@ -44,7 +44,7 @@ jobs:
build-check:
runs-on: macos-12
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Bootstrap
@@ -60,7 +60,7 @@ jobs:
rm -f /usr/local/bin/python3-config
rm -f /usr/local/bin/python3.11-config
brew update
- brew reinstall boost cmake cpputest dbus jsoncpp ninja protobuf pkg-config
+ brew reinstall boost cmake cpputest dbus jsoncpp ninja protobuf@21 pkg-config
- name: Build
run: |
OTBR_OPTIONS='-DOTBR_BORDER_AGENT=OFF -DOTBR_MDNS=OFF -DOT_FIREWALL=OFF -DOTBR_DBUS=OFF' ./script/test build
diff --git a/.github/workflows/meshcop.yml b/.github/workflows/meshcop.yml
index 5837f4f7..e3aaae56 100644
--- a/.github/workflows/meshcop.yml
+++ b/.github/workflows/meshcop.yml
@@ -49,7 +49,7 @@ jobs:
matrix:
mdns: ["avahi"]
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Bootstrap
diff --git a/.github/workflows/openwrt.yml b/.github/workflows/openwrt.yml
index 273142ae..04eaa6ff 100644
--- a/.github/workflows/openwrt.yml
+++ b/.github/workflows/openwrt.yml
@@ -44,7 +44,7 @@ jobs:
openwrt:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: true
- name: Bootstrap
diff --git a/.github/workflows/raspbian.yml b/.github/workflows/raspbian.yml
index c878aaab..47a14909 100644
--- a/.github/workflows/raspbian.yml
+++ b/.github/workflows/raspbian.yml
@@ -48,7 +48,7 @@ jobs:
IMAGE_URL: https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-01-12/2021-01-11-raspios-buster-armhf-lite.zip
BUILD_TARGET: raspbian-gcc
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
submodules: recursive
- name: Bootstrap
diff --git a/CMakeLists.txt b/CMakeLists.txt
index df7c2302..4fefd279 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -57,6 +57,7 @@ if(NOT CMAKE_CXX_STANDARD)
endif()
set(CMAKE_CXX_EXTENSIONS OFF)
+set(CMAKE_EXE_LINKER_FLAGS "-rdynamic ${CMAKE_EXE_LINKER_FLAGS}")
if (OTBR_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
message(STATUS "Coverage: ON")
diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake
index e85e99fe..a8d39058 100644
--- a/etc/cmake/options.cmake
+++ b/etc/cmake/options.cmake
@@ -63,6 +63,11 @@ if (OTBR_FEATURE_FLAGS)
target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_FEATURE_FLAGS=1)
endif()
+option(OTBR_TELEMETRY_DATA_API "Enable telemetry data API support" OFF)
+if (OTBR_TELEMETRY_DATA_API)
+ target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_TELEMETRY_DATA_API=1)
+endif()
+
option(OTBR_DUA_ROUTING "Enable Backbone Router DUA Routing" OFF)
if (OTBR_DUA_ROUTING)
target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_DUA_ROUTING=1)
@@ -114,6 +119,8 @@ endif()
option(OTBR_NAT64 "Enable NAT64 support" OFF)
if(OTBR_NAT64)
target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_NAT64=1)
+else()
+ target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_NAT64=0)
endif()
option(OTBR_VENDOR_INFRA_LINK_SELECT "Enable Vendor-specific infrastructure link selection rules" OFF)
@@ -132,3 +139,15 @@ option(OTBR_PUBLISH_MESHCOP_BA_ID "Publish the MeshCoP mDNS 'id' TXT entry, enab
if (OTBR_PUBLISH_MESHCOP_BA_ID)
target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_PUBLISH_MESHCOP_BA_ID=1)
endif()
+
+option(OTBR_DHCP6_PD "Prefix delegation support" OFF)
+if (OTBR_DHCP6_PD)
+ target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_DHCP6_PD=1)
+else()
+ target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_DHCP6_PD=0)
+endif()
+
+option(OTBR_VENDOR_SERVER "Enable vendor server" OFF)
+if (OTBR_VENDOR_SERVER)
+ target_compile_definitions(otbr-config INTERFACE OTBR_ENABLE_VENDOR_SERVER=1)
+endif()
diff --git a/etc/docker/Dockerfile b/etc/docker/Dockerfile
index 193716b8..eef7a948 100644
--- a/etc/docker/Dockerfile
+++ b/etc/docker/Dockerfile
@@ -42,6 +42,7 @@ ARG RELEASE
ARG REST_API
ARG WEB_GUI
ARG MDNS
+ARG FIREWALL
ENV INFRA_IF_NAME=${INFRA_IF_NAME:-eth0}
ENV BORDER_ROUTING=${BORDER_ROUTING:-1}
@@ -59,6 +60,7 @@ ENV NAT64_DYNAMIC_POOL=${NAT64_DYNAMIC_POOL:-192.168.255.0/24}
ENV DNS64=${DNS64:-0}
ENV WEB_GUI=${WEB_GUI:-1}
ENV REST_API=${REST_API:-1}
+ENV FIREWALL=${FIREWALL:-1}
ENV DOCKER 1
RUN env
@@ -88,6 +90,7 @@ RUN apt-get update \
&& ln -fs /usr/share/zoneinfo/UTC /etc/localtime
COPY ./script /app/script
+COPY ./third_party/mDNSResponder /app/third_party/mDNSResponder
WORKDIR /app
RUN ./script/bootstrap
@@ -106,6 +109,7 @@ RUN ([ "${DNS64}" = "0" ] || chmod 644 /etc/bind/named.conf.options) \
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $OTBR_BUILD_DEPS \
&& ([ "${RELEASE}" = 1 ] || apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false "$OTBR_NORELEASE_DEPS";) \
&& rm -rf /var/lib/apt/lists/* \
+ && rm -rf /tmp/* \
))
ENTRYPOINT ["/app/etc/docker/docker_entrypoint.sh"]
diff --git a/etc/openwrt/openthread-br/Makefile b/etc/openwrt/openthread-br/Makefile
index 1a328c06..cf0fdfb4 100644
--- a/etc/openwrt/openthread-br/Makefile
+++ b/etc/openwrt/openthread-br/Makefile
@@ -51,7 +51,9 @@ CMAKE_OPTIONS+= \
-DOTBR_SRP_ADVERTISING_PROXY=ON \
-DOT_FIREWALL=ON \
-DOT_POSIX_SETTINGS_PATH=\"/etc/openthread\" \
- -DOT_READLINE=OFF
+ -DOT_READLINE=OFF \
+ -DOTBR_NAT64=ON \
+ -DNAT64_SERVICE=\"openthread\"
TARGET_CFLAGS += -DOPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME=\\\"/var/run/openthread-%s\\\"
diff --git a/script/_firewall b/script/_firewall
index 0a29c369..28344d24 100755
--- a/script/_firewall
+++ b/script/_firewall
@@ -31,8 +31,12 @@ FIREWALL_SERVICE=/etc/init.d/otbr-firewall
sudo modprobe ip6table_filter || true
+FIREWALL="${FIREWALL:-1}"
+
firewall_uninstall()
{
+ with FIREWALL || return 0
+
firewall_stop
if have systemctl; then
sudo systemctl disable otbr-firewall || true
@@ -46,6 +50,8 @@ firewall_uninstall()
firewall_install()
{
+ with FIREWALL || return 0
+
sudo cp script/otbr-firewall $FIREWALL_SERVICE
sudo chmod a+x $FIREWALL_SERVICE
if have systemctl; then
@@ -56,6 +62,8 @@ firewall_install()
firewall_start()
{
+ with FIREWALL || return 0
+
if with DOCKER; then
service otbr-firewall start || die 'Failed to start firewall service'
elif have systemctl; then
@@ -65,6 +73,8 @@ firewall_start()
firewall_stop()
{
+ with FIREWALL || return 0
+
if with DOCKER; then
service otbr-firewall stop || true
elif have systemctl; then
diff --git a/script/_nat64 b/script/_nat64
index 9268134e..521c4d22 100644
--- a/script/_nat64
+++ b/script/_nat64
@@ -40,6 +40,7 @@ NAT64_PREFIX=64:ff9b::/96
DYNAMIC_POOL="${NAT64_DYNAMIC_POOL:-192.168.255.0/24}"
NAT44_SERVICE=/etc/init.d/otbr-nat44
WLAN_IFNAMES="${INFRA_IF_NAME:-eth0}"
+THREAD_IF="${THREAD_IF:-wpan0}"
# Currently solution was verified only on raspbian and ubuntu.
#
@@ -156,7 +157,9 @@ EOF
echo " iptables -t nat -A POSTROUTING -o $IFNAME -j MASQUERADE" | sudo tee -a $NAT44_SERVICE
done
else
- echo " iptables -t nat -A POSTROUTING -s \"$DYNAMIC_POOL\" -j MASQUERADE" | sudo tee -a $NAT44_SERVICE
+ # Just a random fwmark bits.
+ echo " iptables -t mangle -A PREROUTING -i $THREAD_IF -j MARK --set-mark 0x1001" | sudo tee -a $NAT44_SERVICE
+ echo " iptables -t nat -A POSTROUTING -m mark --mark 0x1001 -j MASQUERADE" | sudo tee -a $NAT44_SERVICE
for IFNAME in $WLAN_IFNAMES; do
echo " iptables -t filter -A FORWARD -o $IFNAME -j ACCEPT" | sudo tee -a $NAT44_SERVICE
echo " iptables -t filter -A FORWARD -i $IFNAME -j ACCEPT" | sudo tee -a $NAT44_SERVICE
diff --git a/script/_otbr b/script/_otbr
index 790ab10a..dbccc73c 100644
--- a/script/_otbr
+++ b/script/_otbr
@@ -128,6 +128,16 @@ otbr_install()
)
fi
+ if with FIREWALL; then
+ otbr_options+=(
+ "-DOT_FIREWALL=ON"
+ )
+ else
+ otbr_options+=(
+ "-DOT_FIREWALL=OFF"
+ )
+ fi
+
(./script/cmake-build "${otbr_options[@]}" \
&& cd "${OTBR_TOP_BUILDDIR}" \
&& ninja \
diff --git a/script/bootstrap b/script/bootstrap
index 806cc1c8..67d194b1 100755
--- a/script/bootstrap
+++ b/script/bootstrap
@@ -35,6 +35,8 @@
NAT64_SERVICE="${NAT64_SERVICE:-openthread}"
+FIREWALL="${FIREWALL:-1}"
+
install_packages_apt()
{
sudo apt-get update
@@ -56,16 +58,20 @@ install_packages_apt()
# mDNS
sudo apt-get install --no-install-recommends -y libavahi-client3 libavahi-common-dev libavahi-client-dev avahi-daemon
- (MDNS_RESPONDER_SOURCE_NAME=mDNSResponder-1310.80.1 \
+ (MDNS_RESPONDER_SOURCE_NAME=mDNSResponder-1790.80.10 \
+ && MDNS_RESPONDER_PATCH_PATH=$(realpath "$(dirname "$0")"/../third_party/mDNSResponder) \
&& cd /tmp \
&& wget --no-check-certificate https://github.com/apple-oss-distributions/mDNSResponder/archive/refs/tags/$MDNS_RESPONDER_SOURCE_NAME.tar.gz \
&& mkdir -p $MDNS_RESPONDER_SOURCE_NAME \
&& tar xvf $MDNS_RESPONDER_SOURCE_NAME.tar.gz -C $MDNS_RESPONDER_SOURCE_NAME --strip-components=1 \
- && cd /tmp/$MDNS_RESPONDER_SOURCE_NAME/Clients \
- && sed -i '/#include <ctype.h>/a #include <stdarg.h>' dns-sd.c \
- && sed -i '/#include <ctype.h>/a #include <sys/param.h>' dns-sd.c \
- && cd /tmp/$MDNS_RESPONDER_SOURCE_NAME/mDNSPosix \
- && make os=linux && sudo make install os=linux)
+ && cd /tmp/"$MDNS_RESPONDER_SOURCE_NAME" \
+ && (
+ for patch in "$MDNS_RESPONDER_PATCH_PATH"/*.patch; do
+ patch -p1 <"$patch"
+ done
+ ) \
+ && cd mDNSPosix \
+ && make os=linux tls=no && sudo make install os=linux tls=no)
# Boost
sudo apt-get install --no-install-recommends -y libboost-dev libboost-filesystem-dev libboost-system-dev
@@ -100,7 +106,7 @@ install_packages_apt()
sudo apt-get install --no-install-recommends -y libjsoncpp-dev
# reference device
- without REFERENCE_DEVICE || sudo apt-get install --no-install-recommends -y radvd dnsutils
+ without REFERENCE_DEVICE || sudo apt-get install --no-install-recommends -y radvd dnsutils avahi-utils
# backbone-router
without BACKBONE_ROUTER || sudo apt-get install --no-install-recommends -y libnetfilter-queue1 libnetfilter-queue-dev
diff --git a/script/test b/script/test
index 13a7c5e4..f5b955cb 100755
--- a/script/test
+++ b/script/test
@@ -131,6 +131,7 @@ do_build()
"-DOT_THREAD_VERSION=1.3"
"-DOTBR_DBUS=ON"
"-DOTBR_FEATURE_FLAGS=ON"
+ "-DOTBR_TELEMETRY_DATA_API=ON"
"-DOTBR_WEB=ON"
"-DOTBR_UNSECURE_JOIN=ON"
"-DOTBR_TREL=ON"
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ba8f05d9..15790a45 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -31,7 +31,7 @@ if(OTBR_BORDER_AGENT)
add_subdirectory(border_agent)
endif()
add_subdirectory(common)
-if(OTBR_FEATURE_FLAGS)
+if(OTBR_DBUS OR OTBR_FEATURE_FLAGS OR OTBR_TELEMETRY_DATA_API)
add_subdirectory(proto)
endif()
add_subdirectory(ncp)
diff --git a/src/agent/CMakeLists.txt b/src/agent/CMakeLists.txt
index 9a8b7102..60d19b7e 100644
--- a/src/agent/CMakeLists.txt
+++ b/src/agent/CMakeLists.txt
@@ -68,9 +68,9 @@ set(OTBR_AGENT_USER "root" CACHE STRING "set the username running otbr-agent ser
set(OTBR_AGENT_GROUP "root" CACHE STRING "set the group using otbr-agent client")
if(OTBR_MDNS STREQUAL "mDNSResponder")
- set(EXEC_START_PRE "ExecStartPre=service mdns start\n")
+ set(EXEC_START_PRE "ExecStartPre=/usr/sbin/service mdns start\n")
elseif(OTBR_MDNS STREQUAL "avahi")
- set(EXEC_START_PRE "ExecStartPre=service avahi-daemon start\n")
+ set(EXEC_START_PRE "ExecStartPre=/usr/sbin/service avahi-daemon start\n")
else()
message(WARNING "OTBR_MDNS=\"${OTBR_MDNS}\" is not supported")
endif()
diff --git a/src/agent/application.cpp b/src/agent/application.cpp
index c9a5598f..33696024 100644
--- a/src/agent/application.cpp
+++ b/src/agent/application.cpp
@@ -77,7 +77,7 @@ Application::Application(const std::string &aInterfaceName,
, mDBusAgent(mNcp, mBorderAgent.GetPublisher())
#endif
#if OTBR_ENABLE_VENDOR_SERVER
- , mVendorServer(vendor::VendorServer::newInstance(mNcp))
+ , mVendorServer(vendor::VendorServer::newInstance(*this))
#endif
{
OTBR_UNUSED_VARIABLE(aRestListenAddress);
diff --git a/src/agent/application.hpp b/src/agent/application.hpp
index 487206e5..55b39ee6 100644
--- a/src/agent/application.hpp
+++ b/src/agent/application.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_AGENT_APPLICATION_HPP_
#define OTBR_AGENT_APPLICATION_HPP_
+#include "openthread-br/config.h"
+
#include <atomic>
#include <signal.h>
#include <stdint.h>
@@ -62,6 +64,14 @@
namespace otbr {
+#if OTBR_ENABLE_VENDOR_SERVER
+namespace vendor {
+
+class VendorServer;
+
+}
+#endif
+
/**
* @addtogroup border-router-agent
*
@@ -117,6 +127,73 @@ public:
*/
otbrError Run(void);
+ /**
+ * Get the OpenThread controller object the application is using.
+ *
+ * @returns The OpenThread controller object.
+ */
+ Ncp::ControllerOpenThread &GetNcp(void) { return mNcp; }
+
+#if OTBR_ENABLE_BORDER_AGENT
+ /**
+ * Get the border agent the application is using.
+ *
+ * @returns The border agent.
+ */
+ BorderAgent &GetBorderAgent(void)
+ {
+ return mBorderAgent;
+ }
+#endif
+
+#if OTBR_ENABLE_BACKBONE_ROUTER
+ /**
+ * Get the backbone agent the application is using.
+ *
+ * @returns The backbone agent.
+ */
+ BackboneRouter::BackboneAgent &GetBackboneAgent(void)
+ {
+ return mBackboneAgent;
+ }
+#endif
+
+#if OTBR_ENABLE_OPENWRT
+ /**
+ * Get the UBus agent the application is using.
+ *
+ * @returns The UBus agent.
+ */
+ ubus::UBusAgent &GetUBusAgent(void)
+ {
+ return mUbusAgent;
+ }
+#endif
+
+#if OTBR_ENABLE_REST_SERVER
+ /**
+ * Get the rest web server the application is using.
+ *
+ * @returns The rest web server.
+ */
+ rest::RestWebServer &GetRestWebServer(void)
+ {
+ return mRestWebServer;
+ }
+#endif
+
+#if OTBR_ENABLE_DBUS_SERVER
+ /**
+ * Get the DBus agent the application is using.
+ *
+ * @returns The DBus agent.
+ */
+ DBus::DBusAgent &GetDBusAgent(void)
+ {
+ return mDBusAgent;
+ }
+#endif
+
private:
// Default poll timeout.
static const struct timeval kPollTimeout;
diff --git a/src/agent/vendor.hpp b/src/agent/vendor.hpp
index d99b87a8..8e2ecb3a 100644
--- a/src/agent/vendor.hpp
+++ b/src/agent/vendor.hpp
@@ -34,9 +34,14 @@
#ifndef OTBR_AGENT_VENDOR_HPP_
#define OTBR_AGENT_VENDOR_HPP_
-#include "ncp/ncp_openthread.hpp"
+#include "openthread-br/config.h"
+
+#include "agent/application.hpp"
namespace otbr {
+
+class Application;
+
namespace vendor {
/**
@@ -52,11 +57,11 @@ public:
*
* Custom vendor servers should implement this method to return an object of the derived class.
*
- * @param[in] aNcp The OpenThread controller object.
+ * @param[in] aApplication The OTBR application.
*
* @returns New derived VendorServer instance.
*/
- static std::shared_ptr<VendorServer> newInstance(otbr::Ncp::ControllerOpenThread &aNcp);
+ static std::shared_ptr<VendorServer> newInstance(Application &aApplication);
/**
* Initializes the vendor server.
diff --git a/src/android/otdaemon_server.cpp b/src/android/otdaemon_server.cpp
index 4ecfe99e..fc14e9cb 100644
--- a/src/android/otdaemon_server.cpp
+++ b/src/android/otdaemon_server.cpp
@@ -33,6 +33,7 @@
#include <net/if.h>
#include <string.h>
+#include <android-base/file.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <openthread/border_router.h>
@@ -49,9 +50,9 @@ namespace otbr {
namespace vendor {
-std::shared_ptr<VendorServer> VendorServer::newInstance(otbr::Ncp::ControllerOpenThread &aNcp)
+std::shared_ptr<VendorServer> VendorServer::newInstance(Application &aApplication)
{
- return ndk::SharedRefBase::make<Android::OtDaemonServer>(aNcp);
+ return ndk::SharedRefBase::make<Android::OtDaemonServer>(aApplication.GetNcp());
}
} // namespace vendor
@@ -484,5 +485,15 @@ exit:
return status;
}
+binder_status_t OtDaemonServer::dump(int aFd, const char** aArgs, uint32_t aNumArgs)
+{
+ OT_UNUSED_VARIABLE(aArgs);
+ OT_UNUSED_VARIABLE(aNumArgs);
+
+ // TODO: Use ::android::base::WriteStringToFd to dump infomration.
+ fsync(aFd);
+
+ return STATUS_OK;
+}
} // namespace Android
} // namespace otbr
diff --git a/src/android/otdaemon_server.hpp b/src/android/otdaemon_server.hpp
index 06b0c8d5..3f6ff8ff 100644
--- a/src/android/otdaemon_server.hpp
+++ b/src/android/otdaemon_server.hpp
@@ -62,8 +62,8 @@ public:
OtDaemonServer(const OtDaemonServer &) = delete;
void operator=(const OtDaemonServer &) = delete;
- // TODO(wgtdkp): dump service info for debugging.
- // status_t dump(int fd, const Vector<String16> &args) override;
+ // Dump information for debugging.
+ binder_status_t dump(int aFd, const char** aArgs, uint32_t aNumArgs) override;
private:
using DetachCallback = std::function<void()>;
diff --git a/src/backbone_router/backbone_agent.hpp b/src/backbone_router/backbone_agent.hpp
index 6ec62c7b..0ff78083 100644
--- a/src/backbone_router/backbone_agent.hpp
+++ b/src/backbone_router/backbone_agent.hpp
@@ -34,6 +34,8 @@
#ifndef BACKBONE_ROUTER_BACKBONE_AGENT_HPP_
#define BACKBONE_ROUTER_BACKBONE_AGENT_HPP_
+#include "openthread-br/config.h"
+
#if OTBR_ENABLE_BACKBONE_ROUTER
#include <openthread/backbone_router_ftd.h>
diff --git a/src/backbone_router/constants.hpp b/src/backbone_router/constants.hpp
index c853e12f..363e34c3 100644
--- a/src/backbone_router/constants.hpp
+++ b/src/backbone_router/constants.hpp
@@ -34,6 +34,8 @@
#ifndef BACKBONE_CONSTANTS_HPP_
#define BACKBONE_CONSTANTS_HPP_
+#include "openthread-br/config.h"
+
namespace otbr {
namespace BackboneRouter {
diff --git a/src/backbone_router/dua_routing_manager.hpp b/src/backbone_router/dua_routing_manager.hpp
index 97220ca9..daf341df 100644
--- a/src/backbone_router/dua_routing_manager.hpp
+++ b/src/backbone_router/dua_routing_manager.hpp
@@ -34,6 +34,8 @@
#ifndef BACKBONE_ROUTER_DUA_ROUTING_MANAGER
#define BACKBONE_ROUTER_DUA_ROUTING_MANAGER
+#include "openthread-br/config.h"
+
#if OTBR_ENABLE_DUA_ROUTING
#include <set>
diff --git a/src/backbone_router/nd_proxy.hpp b/src/backbone_router/nd_proxy.hpp
index 48a820c9..3a7a2d26 100644
--- a/src/backbone_router/nd_proxy.hpp
+++ b/src/backbone_router/nd_proxy.hpp
@@ -34,6 +34,8 @@
#ifndef ND_PROXY_HPP_
#define ND_PROXY_HPP_
+#include "openthread-br/config.h"
+
#if OTBR_ENABLE_DUA_ROUTING
#ifdef __APPLE__
diff --git a/src/border_agent/border_agent.cpp b/src/border_agent/border_agent.cpp
index 1303687b..78053b10 100644
--- a/src/border_agent/border_agent.cpp
+++ b/src/border_agent/border_agent.cpp
@@ -343,10 +343,11 @@ void AppendVendorTxtEntries(const std::map<std::string, std::vector<uint8_t>> &a
for (auto &addedEntry : aTxtList)
{
- if (addedEntry.mName == key)
+ if (addedEntry.mKey == key)
{
- addedEntry.mValue = value;
- found = true;
+ addedEntry.mValue = value;
+ addedEntry.mIsBooleanAttribute = false;
+ found = true;
break;
}
}
@@ -367,27 +368,23 @@ void BorderAgent::PublishMeshCopService(void)
const otExtAddress *extAddr = otLinkGetExtendedAddress(instance);
const char *networkName = otThreadGetNetworkName(instance);
Mdns::Publisher::TxtList txtList{{"rv", "1"}};
+ Mdns::Publisher::TxtData txtData;
int port;
+ otbrError error;
+
+ OTBR_UNUSED_VARIABLE(error);
otbrLogInfo("Publish meshcop service %s.%s.local.", mServiceInstanceName.c_str(), kBorderAgentServiceType);
#if OTBR_ENABLE_PUBLISH_MESHCOP_BA_ID
{
- otError error;
- uint8_t id[OT_BORDER_AGENT_ID_LENGTH];
- uint16_t idLength = OT_BORDER_AGENT_ID_LENGTH;
+ otError error;
+ otBorderAgentId id;
- error = otBorderAgentGetId(instance, id, &idLength);
+ error = otBorderAgentGetId(instance, &id);
if (error == OT_ERROR_NONE)
{
- if (idLength == OT_BORDER_AGENT_ID_LENGTH)
- {
- txtList.emplace_back("id", id, idLength);
- }
- else
- {
- otbrLogWarning("Border Agent ID length is %d, but expect %d", idLength, OT_BORDER_AGENT_ID_LENGTH);
- }
+ txtList.emplace_back("id", id.mId, sizeof(id));
}
else
{
@@ -441,8 +438,11 @@ void BorderAgent::PublishMeshCopService(void)
port = kBorderAgentServiceDummyPort;
}
+ error = Mdns::Publisher::EncodeTxtData(txtList, txtData);
+ assert(error == OTBR_ERROR_NONE);
+
mPublisher->PublishService(/* aHostName */ "", mServiceInstanceName, kBorderAgentServiceType,
- Mdns::Publisher::SubTypeList{}, port, txtList, [this](otbrError aError) {
+ Mdns::Publisher::SubTypeList{}, port, txtData, [this](otbrError aError) {
if (aError == OTBR_ERROR_ABORTED)
{
// OTBR_ERROR_ABORTED is thrown when an ongoing service registration is
diff --git a/src/border_agent/border_agent.hpp b/src/border_agent/border_agent.hpp
index 896281e6..0274ae18 100644
--- a/src/border_agent/border_agent.hpp
+++ b/src/border_agent/border_agent.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_AGENT_BORDER_AGENT_HPP_
#define OTBR_AGENT_BORDER_AGENT_HPP_
+#include "openthread-br/config.h"
+
#if !(OTBR_ENABLE_MDNS_AVAHI || OTBR_ENABLE_MDNS_MDNSSD || OTBR_ENABLE_MDNS_MOJO)
#error "Border Agent feature requires at least one `OTBR_MDNS` implementation"
#endif
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 1f3aec70..5badd6f6 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -27,7 +27,9 @@
#
add_library(otbr-common
+ api_strings.cpp
byteswap.hpp
+ code_utils.cpp
code_utils.hpp
dns_utils.cpp
logging.cpp
@@ -48,4 +50,6 @@ target_link_libraries(otbr-common
PUBLIC otbr-config
openthread-ftd
openthread-posix
+ $<$<BOOL:${OTBR_FEATURE_FLAGS}>:otbr-proto>
+ $<$<BOOL:${OTBR_TELEMETRY_DATA_API}>:otbr-proto>
)
diff --git a/src/common/api_strings.cpp b/src/common/api_strings.cpp
new file mode 100644
index 00000000..fcb429f7
--- /dev/null
+++ b/src/common/api_strings.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2023, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "common/api_strings.hpp"
+
+std::string GetDeviceRoleName(otDeviceRole aRole)
+{
+ std::string roleName;
+
+ switch (aRole)
+ {
+ case OT_DEVICE_ROLE_DISABLED:
+ roleName = OTBR_ROLE_NAME_DISABLED;
+ break;
+ case OT_DEVICE_ROLE_DETACHED:
+ roleName = OTBR_ROLE_NAME_DETACHED;
+ break;
+ case OT_DEVICE_ROLE_CHILD:
+ roleName = OTBR_ROLE_NAME_CHILD;
+ break;
+ case OT_DEVICE_ROLE_ROUTER:
+ roleName = OTBR_ROLE_NAME_ROUTER;
+ break;
+ case OT_DEVICE_ROLE_LEADER:
+ roleName = OTBR_ROLE_NAME_LEADER;
+ break;
+ }
+
+ return roleName;
+}
diff --git a/src/common/api_strings.hpp b/src/common/api_strings.hpp
new file mode 100644
index 00000000..8b1eb9fc
--- /dev/null
+++ b/src/common/api_strings.hpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * This file has helper functions to convert internal state representations
+ * to string (useful for APIs).
+ *
+ */
+#ifndef OTBR_COMMON_API_STRINGS_HPP_
+#define OTBR_COMMON_API_STRINGS_HPP_
+
+#include "openthread-br/config.h"
+
+#include <string>
+
+#include <openthread/thread.h>
+
+#define OTBR_ROLE_NAME_DISABLED "disabled"
+#define OTBR_ROLE_NAME_DETACHED "detached"
+#define OTBR_ROLE_NAME_CHILD "child"
+#define OTBR_ROLE_NAME_ROUTER "router"
+#define OTBR_ROLE_NAME_LEADER "leader"
+
+std::string GetDeviceRoleName(otDeviceRole aRole);
+
+#endif // OTBR_COMMON_API_STRINGS_HPP_
diff --git a/src/common/byteswap.hpp b/src/common/byteswap.hpp
index 7c3485c2..8c59abc5 100644
--- a/src/common/byteswap.hpp
+++ b/src/common/byteswap.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_COMMON_BYTESWAP_HPP_
#define OTBR_COMMON_BYTESWAP_HPP_
+#include "openthread-br/config.h"
+
#if __APPLE__
#include <libkern/OSByteOrder.h>
#define bswap_16 OSSwapInt16
diff --git a/src/common/callback.hpp b/src/common/callback.hpp
index 45bad8f4..7df19e5d 100644
--- a/src/common/callback.hpp
+++ b/src/common/callback.hpp
@@ -29,6 +29,8 @@
#ifndef OTBR_COMMON_CALLBACK_HPP_
#define OTBR_COMMON_CALLBACK_HPP_
+#include "openthread-br/config.h"
+
#include <functional>
#include <type_traits>
@@ -61,15 +63,27 @@ public:
// compiling issue which trying to instantiate this template constructor
// for use cases like `::mOnceCallback(aOnceCallback)`.
template <typename T, typename = typename std::enable_if<!std::is_same<OnceCallback, T>::value>::type>
- OnceCallback(T &&func)
- : mFunc(std::forward<T>(func))
+ OnceCallback(T &&aFunc)
+ : mFunc(std::forward<T>(aFunc))
+ {
+ }
+
+ OnceCallback(OnceCallback &&aCallback)
+ : mFunc(std::move(aCallback.mFunc))
{
+ aCallback.mFunc = nullptr;
+ }
+
+ OnceCallback &operator=(OnceCallback &&aCallback)
+ {
+ mFunc = std::move(aCallback.mFunc);
+ aCallback.mFunc = nullptr;
+
+ return *this;
}
OnceCallback(const OnceCallback &) = delete;
OnceCallback &operator=(const OnceCallback &) = delete;
- OnceCallback(OnceCallback &&) = default;
- OnceCallback &operator=(OnceCallback &&) = default;
R operator()(Args...) const &
{
@@ -77,13 +91,13 @@ public:
"rvalue, i.e. std::move(callback)().");
}
- R operator()(Args... args) &&
+ R operator()(Args... aArgs) &&
{
// Move `this` to a local variable to clear internal state
// before invoking the callback function.
OnceCallback cb = std::move(*this);
- return cb.mFunc(std::forward<Args>(args)...);
+ return cb.mFunc(std::forward<Args>(aArgs)...);
}
bool IsNull() const { return mFunc == nullptr; }
diff --git a/src/common/code_utils.cpp b/src/common/code_utils.cpp
new file mode 100644
index 00000000..35b8a25e
--- /dev/null
+++ b/src/common/code_utils.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2023, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "common/code_utils.hpp"
+
+uint64_t ConvertOpenThreadUint64(const uint8_t *aValue)
+{
+ uint64_t val = 0;
+
+ for (size_t i = 0; i < sizeof(uint64_t); i++)
+ {
+ val = (val << 8) | aValue[i];
+ }
+ return val;
+}
diff --git a/src/common/code_utils.hpp b/src/common/code_utils.hpp
index ec891133..87791a2b 100644
--- a/src/common/code_utils.hpp
+++ b/src/common/code_utils.hpp
@@ -33,6 +33,8 @@
#ifndef OTBR_COMMON_CODE_UTILS_HPP_
#define OTBR_COMMON_CODE_UTILS_HPP_
+#include "openthread-br/config.h"
+
#ifndef OTBR_LOG_TAG
#define OTBR_LOG_TAG "UTILS"
#endif
@@ -56,6 +58,10 @@
reinterpret_cast<aAlignType>( \
((reinterpret_cast<unsigned long>(aMem) + sizeof(aAlignType) - 1) / sizeof(aAlignType)) * sizeof(aAlignType))
+// Allocate the structure using "raw" storage.
+#define OT_DEFINE_ALIGNED_VAR(name, size, align_type) \
+ align_type name[(((size) + (sizeof(align_type) - 1)) / sizeof(align_type))]
+
#ifndef CONTAINING_RECORD
#define BASE 0x1
#define myoffsetof(s, m) (((size_t) & (((s *)BASE)->m)) - BASE)
@@ -164,6 +170,14 @@ template <typename T, typename... Args> std::unique_ptr<T> MakeUnique(Args &&...
}
/**
+ * This method converts 8 uint8_t bytes into uint64_t using big-endian.
+ *
+ * @param[in] aValue The input 8 uint8_t bytes.
+ * @returns The converted uint64_t.
+ */
+uint64_t ConvertOpenThreadUint64(const uint8_t *aValue);
+
+/**
* This class makes any class that derives from it non-copyable. It is intended to be used as a private base class.
*
*/
diff --git a/src/common/dns_utils.hpp b/src/common/dns_utils.hpp
index 0b3ef5bf..13d8e8c2 100644
--- a/src/common/dns_utils.hpp
+++ b/src/common/dns_utils.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_COMMON_DNS_UTILS_HPP_
#define OTBR_COMMON_DNS_UTILS_HPP_
+#include "openthread-br/config.h"
+
#include "common/types.hpp"
/**
diff --git a/src/dbus/client/client_error.hpp b/src/dbus/client/client_error.hpp
index ad033a26..7f499bdc 100644
--- a/src/dbus/client/client_error.hpp
+++ b/src/dbus/client/client_error.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_DBUS_CLIENT_CLIENT_ERROR_HPP_
#define OTBR_DBUS_CLIENT_CLIENT_ERROR_HPP_
+#include "openthread-br/config.h"
+
#include <string>
#include <dbus/dbus.h>
diff --git a/src/dbus/client/thread_api_dbus.cpp b/src/dbus/client/thread_api_dbus.cpp
index 8e64d15c..0a6c36e6 100644
--- a/src/dbus/client/thread_api_dbus.cpp
+++ b/src/dbus/client/thread_api_dbus.cpp
@@ -29,6 +29,7 @@
#include <map>
#include <string.h>
+#include "common/api_strings.hpp"
#include "common/code_utils.hpp"
#include "dbus/client/client_error.hpp"
#include "dbus/client/thread_api_dbus.hpp"
@@ -638,6 +639,11 @@ ClientError ThreadApiDBus::GetActiveDatasetTlvs(std::vector<uint8_t> &aDataset)
return GetProperty(OTBR_DBUS_PROPERTY_ACTIVE_DATASET_TLVS, aDataset);
}
+ClientError ThreadApiDBus::GetPendingDatasetTlvs(std::vector<uint8_t> &aDataset)
+{
+ return GetProperty(OTBR_DBUS_PROPERTY_PENDING_DATASET_TLVS, aDataset);
+}
+
ClientError ThreadApiDBus::GetFeatureFlagListData(std::vector<uint8_t> &aFeatureFlagListData)
{
return GetProperty(OTBR_DBUS_PROPERTY_FEATURE_FLAG_LIST_DATA, aFeatureFlagListData);
@@ -685,6 +691,16 @@ ClientError ThreadApiDBus::GetDnssdCounters(DnssdCounters &aDnssdCounters)
}
#endif
+ClientError ThreadApiDBus::GetTelemetryData(std::vector<uint8_t> &aTelemetryData)
+{
+ return GetProperty(OTBR_DBUS_PROPERTY_TELEMETRY_DATA, aTelemetryData);
+}
+
+ClientError ThreadApiDBus::GetCapabilities(std::vector<uint8_t> &aCapabilities)
+{
+ return GetProperty(OTBR_DBUS_PROPERTY_CAPABILITIES, aCapabilities);
+}
+
std::string ThreadApiDBus::GetInterfaceName(void)
{
return mInterfaceName;
diff --git a/src/dbus/client/thread_api_dbus.hpp b/src/dbus/client/thread_api_dbus.hpp
index 0a0d149a..622faec7 100644
--- a/src/dbus/client/thread_api_dbus.hpp
+++ b/src/dbus/client/thread_api_dbus.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_THREAD_API_DBUS_HPP_
#define OTBR_THREAD_API_DBUS_HPP_
+#include "openthread-br/config.h"
+
#include <functional>
#include <dbus/dbus.h>
@@ -719,6 +721,18 @@ public:
ClientError GetActiveDatasetTlvs(std::vector<uint8_t> &aDataset);
/**
+ * This method gets the pending operational dataset
+ *
+ * @param[out] aDataset The pending operational dataset
+ *
+ * @retval ERROR_NONE Successfully performed the dbus function call
+ * @retval ERROR_DBUS dbus encode/decode error
+ * @retval ... OpenThread defined error value otherwise
+ *
+ */
+ ClientError GetPendingDatasetTlvs(std::vector<uint8_t> &aDataset);
+
+ /**
* This method gets the feature flag list proto serialized byte data.
*
* @param[out] aFeatureFlagListData The feature flag list proto serialized
@@ -856,6 +870,32 @@ public:
*/
ClientError GetNat64ErrorCounters(Nat64ErrorCounters &aCounters);
+ /**
+ * This method gets the telemetry data proto serialized byte data.
+ *
+ * @param[out] aTelemetryData The telemetry data proto serialized
+ * byte data (see proto/thread_telemetry.proto)
+ *
+ * @retval ERROR_NONE Successfully performed the dbus function call
+ * @retval ERROR_DBUS dbus encode/decode error
+ * @retval ... OpenThread defined error value otherwise
+ *
+ */
+ ClientError GetTelemetryData(std::vector<uint8_t> &aTelemetryData);
+
+ /**
+ * This method gets the capabilities data proto serialized byte data.
+ *
+ * @param[out] aCapabilities The capabilities proto serialized byte data
+ * (see proto/capabilities.proto)
+ *
+ * @retval ERROR_NONE Successfully performed the dbus function call
+ * @retval ERROR_DBUS dbus encode/decode error
+ * @retval ... OpenThread defined error value otherwise
+ *
+ */
+ ClientError GetCapabilities(std::vector<uint8_t> &aCapabilities);
+
private:
ClientError CallDBusMethodSync(const std::string &aMethodName);
ClientError CallDBusMethodAsync(const std::string &aMethodName, DBusPendingCallNotifyFunction aFunction);
diff --git a/src/dbus/common/constants.hpp b/src/dbus/common/constants.hpp
index 1daee19d..1d99f1d7 100644
--- a/src/dbus/common/constants.hpp
+++ b/src/dbus/common/constants.hpp
@@ -93,6 +93,7 @@
#define OTBR_DBUS_PROPERTY_EXTERNAL_ROUTES "ExternalRoutes"
#define OTBR_DBUS_PROPERTY_ON_MESH_PREFIXES "OnMeshPrefixes"
#define OTBR_DBUS_PROPERTY_ACTIVE_DATASET_TLVS "ActiveDatasetTlvs"
+#define OTBR_DBUS_PROPERTY_PENDING_DATASET_TLVS "PendingDatasetTlvs"
#define OTBR_DBUS_PROPERTY_FEATURE_FLAG_LIST_DATA "FeatureFlagListData"
#define OTBR_DBUS_PROPERTY_RADIO_REGION "RadioRegion"
#define OTBR_DBUS_PROPERTY_SRP_SERVER_INFO "SrpServerInfo"
@@ -108,17 +109,14 @@
#define OTBR_DBUS_PROPERTY_RADIO_COEX_METRICS "RadioCoexMetrics"
#define OTBR_DBUS_PROPERTY_BORDER_ROUTING_COUNTERS "BorderRoutingCounters"
#define OTBR_DBUS_PROPERTY_NAT64_STATE "Nat64State"
+#define OTBR_DBUS_PROPERTY_NAT64_CIDR "Nat64Cidr"
#define OTBR_DBUS_PROPERTY_NAT64_MAPPINGS "Nat64Mappings"
#define OTBR_DBUS_PROPERTY_NAT64_PROTOCOL_COUNTERS "Nat64ProtocolCounters"
#define OTBR_DBUS_PROPERTY_NAT64_ERROR_COUNTERS "Nat64ErrorCounters"
#define OTBR_DBUS_PROPERTY_INFRA_LINK_INFO "InfraLinkInfo"
#define OTBR_DBUS_PROPERTY_DNS_UPSTREAM_QUERY_STATE "DnsUpstreamQueryState"
-
-#define OTBR_ROLE_NAME_DISABLED "disabled"
-#define OTBR_ROLE_NAME_DETACHED "detached"
-#define OTBR_ROLE_NAME_CHILD "child"
-#define OTBR_ROLE_NAME_ROUTER "router"
-#define OTBR_ROLE_NAME_LEADER "leader"
+#define OTBR_DBUS_PROPERTY_TELEMETRY_DATA "TelemetryData"
+#define OTBR_DBUS_PROPERTY_CAPABILITIES "Capabilities"
#define OTBR_NAT64_STATE_NAME_DISABLED "disabled"
#define OTBR_NAT64_STATE_NAME_NOT_RUNNING "not_running"
diff --git a/src/dbus/common/dbus_message_dump.hpp b/src/dbus/common/dbus_message_dump.hpp
index d4b462f6..7715c8d8 100644
--- a/src/dbus/common/dbus_message_dump.hpp
+++ b/src/dbus/common/dbus_message_dump.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_AGENT_DBUS_MESSAGE_DUMP_HPP_
#define OTBR_AGENT_DBUS_MESSAGE_DUMP_HPP_
+#include "openthread-br/config.h"
+
#include <dbus/dbus.h>
namespace otbr {
diff --git a/src/dbus/common/dbus_message_helper.hpp b/src/dbus/common/dbus_message_helper.hpp
index 40c61683..03c8f149 100644
--- a/src/dbus/common/dbus_message_helper.hpp
+++ b/src/dbus/common/dbus_message_helper.hpp
@@ -34,6 +34,8 @@
#ifndef DBUS_MESSAGE_HELPER_HPP_
#define DBUS_MESSAGE_HELPER_HPP_
+#include "openthread-br/config.h"
+
#include <array>
#include <string>
#include <tuple>
@@ -212,8 +214,8 @@ template <> struct DBusTypeTrait<ChildInfo>
template <> struct DBusTypeTrait<ActiveScanResult>
{
// struct of { uint64, string, uint64, array<uint8>, uint16, uint16, uint8,
- // uint8, uint8, uint8, bool, bool }
- static constexpr const char *TYPE_AS_STRING = "(tstayqqyyyybb)";
+ // int16, uint8, uint8, bool, bool }
+ static constexpr const char *TYPE_AS_STRING = "(tstayqqynyybb)";
};
template <> struct DBusTypeTrait<EnergyScanResult>
diff --git a/src/dbus/common/dbus_message_helper_openthread.cpp b/src/dbus/common/dbus_message_helper_openthread.cpp
index beee83d2..192279b8 100644
--- a/src/dbus/common/dbus_message_helper_openthread.cpp
+++ b/src/dbus/common/dbus_message_helper_openthread.cpp
@@ -54,6 +54,10 @@ otbrError DBusMessageExtract(DBusMessageIter *aIter, ActiveScanResult &aScanResu
DBusMessageIter sub;
otbrError error = OTBR_ERROR_NONE;
+ // Local variable to help convert an RSSI value.
+ // Dbus doesn't have the concept of a signed byte
+ int16_t rssi = 0;
+
VerifyOrExit(dbus_message_iter_get_arg_type(aIter) == DBUS_TYPE_STRUCT, error = OTBR_ERROR_DBUS);
dbus_message_iter_recurse(aIter, &sub);
@@ -64,12 +68,16 @@ otbrError DBusMessageExtract(DBusMessageIter *aIter, ActiveScanResult &aScanResu
SuccessOrExit(error = DBusMessageExtract(&sub, aScanResult.mPanId));
SuccessOrExit(error = DBusMessageExtract(&sub, aScanResult.mJoinerUdpPort));
SuccessOrExit(error = DBusMessageExtract(&sub, aScanResult.mChannel));
- SuccessOrExit(error = DBusMessageExtract(&sub, aScanResult.mRssi));
+ SuccessOrExit(error = DBusMessageExtract<int16_t>(&sub, rssi));
SuccessOrExit(error = DBusMessageExtract(&sub, aScanResult.mLqi));
SuccessOrExit(error = DBusMessageExtract(&sub, aScanResult.mVersion));
SuccessOrExit(error = DBusMessageExtract(&sub, aScanResult.mIsNative));
SuccessOrExit(error = DBusMessageExtract(&sub, aScanResult.mDiscover));
+ // Double check the value is within int8 bounds and cast back
+ VerifyOrExit((rssi <= INT8_MAX) && (rssi >= INT8_MIN), error = OTBR_ERROR_PARSE);
+ aScanResult.mRssi = static_cast<int8_t>(rssi);
+
dbus_message_iter_next(aIter);
error = OTBR_ERROR_NONE;
exit:
@@ -89,7 +97,10 @@ otbrError DBusMessageEncode(DBusMessageIter *aIter, const ActiveScanResult &aSca
SuccessOrExit(error = DBusMessageEncode(&sub, aScanResult.mPanId));
SuccessOrExit(error = DBusMessageEncode(&sub, aScanResult.mJoinerUdpPort));
SuccessOrExit(error = DBusMessageEncode(&sub, aScanResult.mChannel));
- SuccessOrExit(error = DBusMessageEncode(&sub, aScanResult.mRssi));
+
+ // Dbus doesn't have a signed byte, cast into an int16
+ SuccessOrExit(error = DBusMessageEncode(&sub, static_cast<int16_t>(aScanResult.mRssi)));
+
SuccessOrExit(error = DBusMessageEncode(&sub, aScanResult.mLqi));
SuccessOrExit(error = DBusMessageEncode(&sub, aScanResult.mVersion));
SuccessOrExit(error = DBusMessageEncode(&sub, aScanResult.mIsNative));
diff --git a/src/dbus/common/dbus_resources.hpp b/src/dbus/common/dbus_resources.hpp
index 06ba338e..edc7dba6 100644
--- a/src/dbus/common/dbus_resources.hpp
+++ b/src/dbus/common/dbus_resources.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_DBUS_DBUS_RESOURCES_HPP_
#define OTBR_DBUS_DBUS_RESOURCES_HPP_
+#include "openthread-br/config.h"
+
#include <memory>
#include <utility>
diff --git a/src/dbus/common/error.hpp b/src/dbus/common/error.hpp
index 8eed9d65..8b7a2239 100644
--- a/src/dbus/common/error.hpp
+++ b/src/dbus/common/error.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_DBUS_COMMON_ERROR_HPP_
#define OTBR_DBUS_COMMON_ERROR_HPP_
+#include "openthread-br/config.h"
+
#include <string>
#include <dbus/dbus.h>
diff --git a/src/dbus/common/types.hpp b/src/dbus/common/types.hpp
index 2510e90c..15a49abc 100644
--- a/src/dbus/common/types.hpp
+++ b/src/dbus/common/types.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_DBUS_COMMON_TYPES_HPP_
#define OTBR_DBUS_COMMON_TYPES_HPP_
+#include "openthread-br/config.h"
+
#include "dbus/common/error.hpp"
#include <stdint.h>
diff --git a/src/dbus/server/CMakeLists.txt b/src/dbus/server/CMakeLists.txt
index 5136fc03..2d0ae640 100644
--- a/src/dbus/server/CMakeLists.txt
+++ b/src/dbus/server/CMakeLists.txt
@@ -43,14 +43,14 @@ add_library(otbr-dbus-server STATIC
target_include_directories(otbr-dbus-server PRIVATE
${PROJECT_BINARY_DIR}/src
- $<$<BOOL:${OTBR_FEATURE_FLAGS}>:${PROJECT_SOURCE_DIR}/build/src>
+ ${PROJECT_SOURCE_DIR}/build/src
)
add_dependencies(otbr-dbus-server otbr-dbus-introspect-header)
target_link_libraries(otbr-dbus-server PUBLIC
otbr-dbus-common
- $<$<BOOL:${OTBR_FEATURE_FLAGS}>:otbr-proto>
+ otbr-proto
)
if(OTBR_DOC)
diff --git a/src/dbus/server/dbus_agent.hpp b/src/dbus/server/dbus_agent.hpp
index 51f815da..c9308ec3 100644
--- a/src/dbus/server/dbus_agent.hpp
+++ b/src/dbus/server/dbus_agent.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_DBUS_AGENT_HPP_
#define OTBR_DBUS_AGENT_HPP_
+#include "openthread-br/config.h"
+
#include <functional>
#include <set>
#include <string>
diff --git a/src/dbus/server/dbus_object.cpp b/src/dbus/server/dbus_object.cpp
index 65e09353..64c384a6 100644
--- a/src/dbus/server/dbus_object.cpp
+++ b/src/dbus/server/dbus_object.cpp
@@ -115,7 +115,7 @@ DBusHandlerResult DBusObject::MessageHandler(DBusConnection *aConnection, DBusMe
if (dbus_message_get_type(aMessage) == DBUS_MESSAGE_TYPE_METHOD_CALL && iter != mMethodHandlers.end())
{
- otbrLogInfo("Handling method %s", memberName.c_str());
+ otbrLogDebug("Handling method %s", memberName.c_str());
if (otbrLogGetLevel() >= OTBR_LOG_DEBUG)
{
DumpDBusMessage(*aMessage);
@@ -144,7 +144,7 @@ void DBusObject::GetPropertyMethodHandler(DBusRequest &aRequest)
{
auto propertyIter = mGetPropertyHandlers.find(interfaceName);
- otbrLogInfo("GetProperty %s.%s", interfaceName.c_str(), propertyName.c_str());
+ otbrLogDebug("GetProperty %s.%s", interfaceName.c_str(), propertyName.c_str());
VerifyOrExit(propertyIter != mGetPropertyHandlers.end(), error = OT_ERROR_NOT_FOUND);
{
DBusMessageIter replyIter;
diff --git a/src/dbus/server/dbus_object.hpp b/src/dbus/server/dbus_object.hpp
index ebe334fa..8aa335ba 100644
--- a/src/dbus/server/dbus_object.hpp
+++ b/src/dbus/server/dbus_object.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_DBUS_DBUS_OBJECT_HPP_
#define OTBR_DBUS_DBUS_OBJECT_HPP_
+#include "openthread-br/config.h"
+
#ifndef OTBR_LOG_TAG
#define OTBR_LOG_TAG "DBUS"
#endif
diff --git a/src/dbus/server/dbus_request.hpp b/src/dbus/server/dbus_request.hpp
index 6f671220..2e712865 100644
--- a/src/dbus/server/dbus_request.hpp
+++ b/src/dbus/server/dbus_request.hpp
@@ -31,6 +31,11 @@
* This file includes definitions for a d-bus request.
*/
+#ifndef DBUS_SERVER_DBUS_REQUEST_HPP_
+#define DBUS_SERVER_DBUS_REQUEST_HPP_
+
+#include "openthread-br/config.h"
+
#ifndef OTBR_LOG_TAG
#define OTBR_LOG_TAG "DBUS"
#endif
@@ -218,3 +223,5 @@ private:
} // namespace DBus
} // namespace otbr
+
+#endif // DBUS_SERVER_DBUS_REQUEST_HPP_
diff --git a/src/dbus/server/dbus_thread_object.cpp b/src/dbus/server/dbus_thread_object.cpp
index 8a5019f9..b183c650 100644
--- a/src/dbus/server/dbus_thread_object.cpp
+++ b/src/dbus/server/dbus_thread_object.cpp
@@ -44,43 +44,23 @@
#include <openthread/thread_ftd.h>
#include <openthread/platform/radio.h>
+#include "common/api_strings.hpp"
#include "common/byteswap.hpp"
+#include "common/code_utils.hpp"
#include "dbus/common/constants.hpp"
#include "dbus/server/dbus_agent.hpp"
#include "dbus/server/dbus_thread_object.hpp"
#if OTBR_ENABLE_FEATURE_FLAGS
#include "proto/feature_flag.pb.h"
#endif
+#if OTBR_ENABLE_TELEMETRY_DATA_API
+#include "proto/thread_telemetry.pb.h"
+#endif
+#include "proto/capabilities.pb.h"
using std::placeholders::_1;
using std::placeholders::_2;
-static std::string GetDeviceRoleName(otDeviceRole aRole)
-{
- std::string roleName;
-
- switch (aRole)
- {
- case OT_DEVICE_ROLE_DISABLED:
- roleName = OTBR_ROLE_NAME_DISABLED;
- break;
- case OT_DEVICE_ROLE_DETACHED:
- roleName = OTBR_ROLE_NAME_DETACHED;
- break;
- case OT_DEVICE_ROLE_CHILD:
- roleName = OTBR_ROLE_NAME_CHILD;
- break;
- case OT_DEVICE_ROLE_ROUTER:
- roleName = OTBR_ROLE_NAME_ROUTER;
- break;
- case OT_DEVICE_ROLE_LEADER:
- roleName = OTBR_ROLE_NAME_LEADER;
- break;
- }
-
- return roleName;
-}
-
#if OTBR_ENABLE_NAT64
static std::string GetNat64StateName(otNat64State aState)
{
@@ -106,17 +86,6 @@ static std::string GetNat64StateName(otNat64State aState)
}
#endif // OTBR_ENABLE_NAT64
-static uint64_t ConvertOpenThreadUint64(const uint8_t *aValue)
-{
- uint64_t val = 0;
-
- for (size_t i = 0; i < sizeof(uint64_t); i++)
- {
- val = (val << 8) | aValue[i];
- }
- return val;
-}
-
namespace otbr {
namespace DBus {
@@ -193,6 +162,8 @@ otbrError DBusThreadObject::Init(void)
std::bind(&DBusThreadObject::SetRadioRegionHandler, this, _1));
RegisterSetPropertyHandler(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_PROPERTY_DNS_UPSTREAM_QUERY_STATE,
std::bind(&DBusThreadObject::SetDnsUpstreamQueryState, this, _1));
+ RegisterSetPropertyHandler(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_PROPERTY_NAT64_CIDR,
+ std::bind(&DBusThreadObject::SetNat64Cidr, this, _1));
RegisterGetPropertyHandler(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_PROPERTY_LINK_MODE,
std::bind(&DBusThreadObject::GetLinkModeHandler, this, _1));
@@ -257,6 +228,8 @@ otbrError DBusThreadObject::Init(void)
std::bind(&DBusThreadObject::GetOnMeshPrefixesHandler, this, _1));
RegisterGetPropertyHandler(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_PROPERTY_ACTIVE_DATASET_TLVS,
std::bind(&DBusThreadObject::GetActiveDatasetTlvsHandler, this, _1));
+ RegisterGetPropertyHandler(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_PROPERTY_PENDING_DATASET_TLVS,
+ std::bind(&DBusThreadObject::GetPendingDatasetTlvsHandler, this, _1));
RegisterGetPropertyHandler(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_PROPERTY_FEATURE_FLAG_LIST_DATA,
std::bind(&DBusThreadObject::GetFeatureFlagListDataHandler, this, _1));
RegisterGetPropertyHandler(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_PROPERTY_RADIO_REGION,
@@ -291,10 +264,16 @@ otbrError DBusThreadObject::Init(void)
std::bind(&DBusThreadObject::GetNat64ProtocolCounters, this, _1));
RegisterGetPropertyHandler(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_PROPERTY_NAT64_ERROR_COUNTERS,
std::bind(&DBusThreadObject::GetNat64ErrorCounters, this, _1));
+ RegisterGetPropertyHandler(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_PROPERTY_NAT64_CIDR,
+ std::bind(&DBusThreadObject::GetNat64Cidr, this, _1));
RegisterGetPropertyHandler(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_PROPERTY_INFRA_LINK_INFO,
std::bind(&DBusThreadObject::GetInfraLinkInfo, this, _1));
RegisterGetPropertyHandler(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_PROPERTY_DNS_UPSTREAM_QUERY_STATE,
std::bind(&DBusThreadObject::GetDnsUpstreamQueryState, this, _1));
+ RegisterGetPropertyHandler(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_PROPERTY_TELEMETRY_DATA,
+ std::bind(&DBusThreadObject::GetTelemetryDataHandler, this, _1));
+ RegisterGetPropertyHandler(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_PROPERTY_CAPABILITIES,
+ std::bind(&DBusThreadObject::GetCapabilitiesHandler, this, _1));
SuccessOrExit(error = Signal(OTBR_DBUS_THREAD_INTERFACE, OTBR_DBUS_SIGNAL_READY, std::make_tuple()));
@@ -336,7 +315,7 @@ void DBusThreadObject::ReplyScanResult(DBusRequest &aR
{
for (const auto &r : aResult)
{
- ActiveScanResult result;
+ ActiveScanResult result = {};
result.mExtAddress = ConvertOpenThreadUint64(r.mExtAddress.m8);
result.mPanId = r.mPanId;
@@ -1209,6 +1188,22 @@ exit:
return error;
}
+otError DBusThreadObject::GetPendingDatasetTlvsHandler(DBusMessageIter &aIter)
+{
+ auto threadHelper = mNcp->GetThreadHelper();
+ otError error = OT_ERROR_NONE;
+ std::vector<uint8_t> data;
+ otOperationalDatasetTlvs datasetTlvs;
+
+ SuccessOrExit(error = otDatasetGetPendingTlvs(threadHelper->GetInstance(), &datasetTlvs));
+ data = std::vector<uint8_t>{std::begin(datasetTlvs.mTlvs), std::begin(datasetTlvs.mTlvs) + datasetTlvs.mLength};
+
+ VerifyOrExit(DBusMessageEncodeToVariant(&aIter, data) == OTBR_ERROR_NONE, error = OT_ERROR_INVALID_ARGS);
+
+exit:
+ return error;
+}
+
otError DBusThreadObject::SetFeatureFlagListDataHandler(DBusMessageIter &aIter)
{
#if OTBR_ENABLE_FEATURE_FLAGS
@@ -1410,6 +1405,48 @@ exit:
#endif // OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
}
+otError DBusThreadObject::GetTelemetryDataHandler(DBusMessageIter &aIter)
+{
+#if OTBR_ENABLE_TELEMETRY_DATA_API
+ otError error = OT_ERROR_NONE;
+ threadnetwork::TelemetryData telemetryData;
+ auto threadHelper = mNcp->GetThreadHelper();
+
+ VerifyOrExit(threadHelper->RetrieveTelemetryData(mPublisher, telemetryData) == OT_ERROR_NONE);
+
+ {
+ const std::string telemetryDataBytes = telemetryData.SerializeAsString();
+ std::vector<uint8_t> data(telemetryDataBytes.begin(), telemetryDataBytes.end());
+
+ VerifyOrExit(DBusMessageEncodeToVariant(&aIter, data) == OTBR_ERROR_NONE, error = OT_ERROR_INVALID_ARGS);
+ }
+
+exit:
+ return error;
+#else
+ OTBR_UNUSED_VARIABLE(aIter);
+ return OT_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+otError DBusThreadObject::GetCapabilitiesHandler(DBusMessageIter &aIter)
+{
+ otError error = OT_ERROR_NONE;
+ otbr::Capabilities capabilities;
+
+ capabilities.set_nat64(OTBR_ENABLE_NAT64);
+
+ {
+ const std::string dataBytes = capabilities.SerializeAsString();
+ std::vector<uint8_t> data(dataBytes.begin(), dataBytes.end());
+
+ VerifyOrExit(DBusMessageEncodeToVariant(&aIter, data) == OTBR_ERROR_NONE, error = OT_ERROR_INVALID_ARGS);
+ }
+
+exit:
+ return error;
+}
+
void DBusThreadObject::GetPropertiesHandler(DBusRequest &aRequest)
{
UniqueDBusMessage reply(dbus_message_new_method_return(aRequest.GetMessage()));
@@ -1787,6 +1824,36 @@ exit:
return error;
}
+otError DBusThreadObject::GetNat64Cidr(DBusMessageIter &aIter)
+{
+ otError error = OT_ERROR_NONE;
+
+ otIp4Cidr cidr;
+ char cidrString[OT_IP4_CIDR_STRING_SIZE];
+
+ otNat64GetCidr(mNcp->GetThreadHelper()->GetInstance(), &cidr);
+ otIp4CidrToString(&cidr, cidrString, sizeof(cidrString));
+
+ VerifyOrExit(DBusMessageEncodeToVariant(&aIter, std::string(cidrString)) == OTBR_ERROR_NONE,
+ error = OT_ERROR_INVALID_ARGS);
+
+exit:
+ return error;
+}
+
+otError DBusThreadObject::SetNat64Cidr(DBusMessageIter &aIter)
+{
+ otError error = OT_ERROR_NONE;
+ std::string cidrString;
+ otIp4Cidr cidr;
+
+ VerifyOrExit(DBusMessageExtractFromVariant(&aIter, cidrString) == OTBR_ERROR_NONE, error = OT_ERROR_INVALID_ARGS);
+ otIp4CidrFromString(cidrString.c_str(), &cidr);
+ SuccessOrExit(error = otNat64SetIp4Cidr(mNcp->GetThreadHelper()->GetInstance(), &cidr));
+
+exit:
+ return error;
+}
#else // OTBR_ENABLE_NAT64
void DBusThreadObject::SetNat64Enabled(DBusRequest &aRequest)
{
@@ -1817,6 +1884,18 @@ otError DBusThreadObject::GetNat64ErrorCounters(DBusMessageIter &aIter)
OTBR_UNUSED_VARIABLE(aIter);
return OT_ERROR_NOT_IMPLEMENTED;
}
+
+otError DBusThreadObject::GetNat64Cidr(DBusMessageIter &aIter)
+{
+ OTBR_UNUSED_VARIABLE(aIter);
+ return OT_ERROR_NOT_IMPLEMENTED;
+}
+
+otError DBusThreadObject::SetNat64Cidr(DBusMessageIter &aIter)
+{
+ OTBR_UNUSED_VARIABLE(aIter);
+ return OT_ERROR_NOT_IMPLEMENTED;
+}
#endif // OTBR_ENABLE_NAT64
otError DBusThreadObject::GetInfraLinkInfo(DBusMessageIter &aIter)
diff --git a/src/dbus/server/dbus_thread_object.hpp b/src/dbus/server/dbus_thread_object.hpp
index 6fefecef..9b23bd6e 100644
--- a/src/dbus/server/dbus_thread_object.hpp
+++ b/src/dbus/server/dbus_thread_object.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_DBUS_THREAD_OBJECT_HPP_
#define OTBR_DBUS_THREAD_OBJECT_HPP_
+#include "openthread-br/config.h"
+
#include <string>
#include <openthread/link.h>
@@ -115,6 +117,7 @@ private:
otError SetFeatureFlagListDataHandler(DBusMessageIter &aIter);
otError SetRadioRegionHandler(DBusMessageIter &aIter);
otError SetDnsUpstreamQueryState(DBusMessageIter &aIter);
+ otError SetNat64Cidr(DBusMessageIter &aIter);
otError GetLinkModeHandler(DBusMessageIter &aIter);
otError GetDeviceRoleHandler(DBusMessageIter &aIter);
@@ -146,6 +149,7 @@ private:
otError GetExternalRoutesHandler(DBusMessageIter &aIter);
otError GetOnMeshPrefixesHandler(DBusMessageIter &aIter);
otError GetActiveDatasetTlvsHandler(DBusMessageIter &aIter);
+ otError GetPendingDatasetTlvsHandler(DBusMessageIter &aIter);
otError GetFeatureFlagListDataHandler(DBusMessageIter &aIter);
otError GetRadioRegionHandler(DBusMessageIter &aIter);
otError GetSrpServerInfoHandler(DBusMessageIter &aIter);
@@ -160,11 +164,14 @@ private:
otError GetRadioCoexMetrics(DBusMessageIter &aIter);
otError GetBorderRoutingCountersHandler(DBusMessageIter &aIter);
otError GetNat64State(DBusMessageIter &aIter);
+ otError GetNat64Cidr(DBusMessageIter &aIter);
otError GetNat64Mappings(DBusMessageIter &aIter);
otError GetNat64ProtocolCounters(DBusMessageIter &aIter);
otError GetNat64ErrorCounters(DBusMessageIter &aIter);
otError GetInfraLinkInfo(DBusMessageIter &aIter);
otError GetDnsUpstreamQueryState(DBusMessageIter &aIter);
+ otError GetTelemetryDataHandler(DBusMessageIter &aIter);
+ otError GetCapabilitiesHandler(DBusMessageIter &aIter);
void ReplyScanResult(DBusRequest &aRequest, otError aError, const std::vector<otActiveScanResult> &aResult);
void ReplyEnergyScanResult(DBusRequest &aRequest, otError aError, const std::vector<otEnergyScanResult> &aResult);
diff --git a/src/dbus/server/error_helper.hpp b/src/dbus/server/error_helper.hpp
index 1016639f..6b35ac1d 100644
--- a/src/dbus/server/error_helper.hpp
+++ b/src/dbus/server/error_helper.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_DBUS_SERVER_ERROR_HELPER_HPP_
#define OTBR_DBUS_SERVER_ERROR_HELPER_HPP_
+#include "openthread-br/config.h"
+
#include <string>
#include <dbus/dbus.h>
diff --git a/src/dbus/server/introspect.xml b/src/dbus/server/introspect.xml
index 227e8aff..0a898181 100644
--- a/src/dbus/server/introspect.xml
+++ b/src/dbus/server/introspect.xml
@@ -9,15 +9,22 @@
<literallayout>
struct {
uint64 ext_address
+ string network_name
+ uint64 ext_panid
+ uint8[] steering_data
uint16 panid
- uint16 channel
- uint16 rssi
+ uint16 joiner_udp_port
+ uint8 channel
+ int16 rssi
uint8 lqi
+ uint8 version
+ bool is_native
+ bool is_joinable
}
</literallayout>
-->
<method name="Scan">
- <arg name="scan_result" type="a(tqqqy)" direction="out"/>
+ <arg name="scan_result" type="a(tstayqqynyybb)" direction="out"/>
</method>
<!-- Energy Scan: Perform a Thread energy scan.
@@ -529,6 +536,11 @@
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
+ <!-- PendingDatasetTlvs: The Thread pending dataset tlv in binary form. -->
+ <property name="PendingDatasetTlvs" type="ay" access="read">
+ <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
+ </property>
+
<!-- FeatureFlagListData: The Thread feature flags (defined as proto/feature_flag.proto)
in binary form. -->
<property name="FeatureFlagListData" type="ay" access="readwrite">
@@ -861,6 +873,11 @@
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
+ <!-- Nat64Cidr: The CIDR used for NAT64 -->
+ <property name="Nat64Cidr" type="s" access="readwrite">
+ <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
+ </property>
+
<!-- BorderRoutingCounters: The counters for inbound/outbound packets
<literallayout>
struct {
@@ -916,6 +933,18 @@
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
</property>
+ <!-- TelemetryData: The Thread telemetry data (defined as proto/thread_telemetry.proto)
+ in binary form. -->
+ <property name="TelemetryData" type="ay" access="read">
+ <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
+ </property>
+
+ <!-- Capabilities: The Thread capabilities data (defined as proto/capabilities.proto)
+ in binary form. -->
+ <property name="Capabilities" type="ay" access="read">
+ <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
+ </property>
+
<!-- The Ready signal is sent on start -->
<signal name="Ready">
</signal>
diff --git a/src/mdns/mdns.cpp b/src/mdns/mdns.cpp
index 9646d551..d7e09353 100644
--- a/src/mdns/mdns.cpp
+++ b/src/mdns/mdns.cpp
@@ -52,23 +52,21 @@ void Publisher::PublishService(const std::string &aHostName,
const std::string &aType,
const SubTypeList &aSubTypeList,
uint16_t aPort,
- const TxtList &aTxtList,
+ const TxtData &aTxtData,
ResultCallback &&aCallback)
{
otbrError error;
mServiceRegistrationBeginTime[std::make_pair(aName, aType)] = Clock::now();
- error = PublishServiceImpl(aHostName, aName, aType, aSubTypeList, aPort, aTxtList, std::move(aCallback));
+ error = PublishServiceImpl(aHostName, aName, aType, aSubTypeList, aPort, aTxtData, std::move(aCallback));
if (error != OTBR_ERROR_NONE)
{
UpdateMdnsResponseCounters(mTelemetryInfo.mServiceRegistrations, error);
}
}
-void Publisher::PublishHost(const std::string &aName,
- const std::vector<Ip6Address> &aAddresses,
- ResultCallback &&aCallback)
+void Publisher::PublishHost(const std::string &aName, const AddressList &aAddresses, ResultCallback &&aCallback)
{
otbrError error;
@@ -81,14 +79,14 @@ void Publisher::PublishHost(const std::string &aName,
}
}
-void Publisher::OnServiceResolveFailed(const std::string &aType, const std::string &aInstanceName, int32_t aErrorCode)
+void Publisher::OnServiceResolveFailed(std::string aType, std::string aInstanceName, int32_t aErrorCode)
{
UpdateMdnsResponseCounters(mTelemetryInfo.mServiceResolutions, DnsErrorToOtbrError(aErrorCode));
UpdateServiceInstanceResolutionEmaLatency(aInstanceName, aType, DnsErrorToOtbrError(aErrorCode));
OnServiceResolveFailedImpl(aType, aInstanceName, aErrorCode);
}
-void Publisher::OnHostResolveFailed(const std::string &aHostName, int32_t aErrorCode)
+void Publisher::OnHostResolveFailed(std::string aHostName, int32_t aErrorCode)
{
UpdateMdnsResponseCounters(mTelemetryInfo.mHostResolutions, DnsErrorToOtbrError(aErrorCode));
UpdateHostResolutionEmaLatency(aHostName, DnsErrorToOtbrError(aErrorCode));
@@ -99,18 +97,32 @@ otbrError Publisher::EncodeTxtData(const TxtList &aTxtList, std::vector<uint8_t>
{
otbrError error = OTBR_ERROR_NONE;
- for (const auto &txtEntry : aTxtList)
+ aTxtData.clear();
+
+ for (const TxtEntry &txtEntry : aTxtList)
{
- const auto &name = txtEntry.mName;
- const auto &value = txtEntry.mValue;
- const size_t entryLength = name.length() + 1 + value.size();
+ size_t entryLength = txtEntry.mKey.length();
+
+ if (!txtEntry.mIsBooleanAttribute)
+ {
+ entryLength += txtEntry.mValue.size() + sizeof(uint8_t); // for `=` char.
+ }
VerifyOrExit(entryLength <= kMaxTextEntrySize, error = OTBR_ERROR_INVALID_ARGS);
aTxtData.push_back(static_cast<uint8_t>(entryLength));
- aTxtData.insert(aTxtData.end(), name.begin(), name.end());
- aTxtData.push_back('=');
- aTxtData.insert(aTxtData.end(), value.begin(), value.end());
+ aTxtData.insert(aTxtData.end(), txtEntry.mKey.begin(), txtEntry.mKey.end());
+
+ if (!txtEntry.mIsBooleanAttribute)
+ {
+ aTxtData.push_back('=');
+ aTxtData.insert(aTxtData.end(), txtEntry.mValue.begin(), txtEntry.mValue.end());
+ }
+ }
+
+ if (aTxtData.empty())
+ {
+ aTxtData.push_back(0);
}
exit:
@@ -121,30 +133,39 @@ otbrError Publisher::DecodeTxtData(Publisher::TxtList &aTxtList, const uint8_t *
{
otbrError error = OTBR_ERROR_NONE;
+ aTxtList.clear();
+
for (uint16_t r = 0; r < aTxtLength;)
{
uint16_t entrySize = aTxtData[r];
uint16_t keyStart = r + 1;
uint16_t entryEnd = keyStart + entrySize;
uint16_t keyEnd = keyStart;
- uint16_t valStart;
+
+ VerifyOrExit(entryEnd <= aTxtLength, error = OTBR_ERROR_PARSE);
while (keyEnd < entryEnd && aTxtData[keyEnd] != '=')
{
keyEnd++;
}
- valStart = keyEnd;
- if (valStart < entryEnd && aTxtData[valStart] == '=')
+ if (keyEnd == entryEnd)
{
- valStart++;
+ if (keyEnd > keyStart)
+ {
+ // No `=`, treat as a boolean attribute.
+ aTxtList.emplace_back(reinterpret_cast<const char *>(&aTxtData[keyStart]), keyEnd - keyStart);
+ }
}
+ else
+ {
+ uint16_t valStart = keyEnd + 1; // To skip over `=`
- aTxtList.emplace_back(reinterpret_cast<const char *>(&aTxtData[keyStart]), keyEnd - keyStart,
- &aTxtData[valStart], entryEnd - valStart);
+ aTxtList.emplace_back(reinterpret_cast<const char *>(&aTxtData[keyStart]), keyEnd - keyStart,
+ &aTxtData[valStart], entryEnd - valStart);
+ }
r += entrySize + 1;
- VerifyOrExit(r <= aTxtLength, error = OTBR_ERROR_PARSE);
}
exit:
@@ -175,7 +196,7 @@ uint64_t Publisher::AddSubscriptionCallbacks(Publisher::DiscoveredServiceInstanc
return subscriberId;
}
-void Publisher::OnServiceResolved(const std::string &aType, const DiscoveredInstanceInfo &aInstanceInfo)
+void Publisher::OnServiceResolved(std::string aType, DiscoveredInstanceInfo aInstanceInfo)
{
std::vector<uint64_t> subscriberIds;
@@ -216,7 +237,7 @@ void Publisher::OnServiceResolved(const std::string &aType, const DiscoveredInst
}
}
-void Publisher::OnServiceRemoved(uint32_t aNetifIndex, const std::string &aType, const std::string &aInstanceName)
+void Publisher::OnServiceRemoved(uint32_t aNetifIndex, std::string aType, std::string aInstanceName)
{
DiscoveredInstanceInfo instanceInfo;
@@ -229,7 +250,7 @@ void Publisher::OnServiceRemoved(uint32_t aNetifIndex, const std::string &aType,
OnServiceResolved(aType, instanceInfo);
}
-void Publisher::OnHostResolved(const std::string &aHostName, const Publisher::DiscoveredHostInfo &aHostInfo)
+void Publisher::OnHostResolved(std::string aHostName, Publisher::DiscoveredHostInfo aHostInfo)
{
otbrLogInfo("Host %s is resolved successfully: host %s addresses %zu ttl %u", aHostName.c_str(),
aHostInfo.mHostName.c_str(), aHostInfo.mAddresses.size(), aHostInfo.mTtl);
@@ -257,13 +278,6 @@ Publisher::SubTypeList Publisher::SortSubTypeList(SubTypeList aSubTypeList)
return aSubTypeList;
}
-Publisher::TxtList Publisher::SortTxtList(TxtList aTxtList)
-{
- std::sort(aTxtList.begin(), aTxtList.end(),
- [](const TxtEntry &aLhs, const TxtEntry &aRhs) { return aLhs.mName < aRhs.mName; });
- return aTxtList;
-}
-
Publisher::AddressList Publisher::SortAddressList(AddressList aAddressList)
{
std::sort(aAddressList.begin(), aAddressList.end());
@@ -316,14 +330,14 @@ Publisher::ResultCallback Publisher::HandleDuplicateServiceRegistration(const st
const std::string &aType,
const SubTypeList &aSubTypeList,
uint16_t aPort,
- const TxtList &aTxtList,
+ const TxtData &aTxtData,
ResultCallback &&aCallback)
{
ServiceRegistration *serviceReg = FindServiceRegistration(aName, aType);
VerifyOrExit(serviceReg != nullptr);
- if (serviceReg->IsOutdated(aHostName, aName, aType, aSubTypeList, aPort, aTxtList))
+ if (serviceReg->IsOutdated(aHostName, aName, aType, aSubTypeList, aPort, aTxtData))
{
otbrLogInfo("Removing existing service %s.%s: outdated", aName.c_str(), aType.c_str());
RemoveServiceRegistration(aName, aType, OTBR_ERROR_ABORTED);
@@ -352,9 +366,9 @@ exit:
return std::move(aCallback);
}
-Publisher::ResultCallback Publisher::HandleDuplicateHostRegistration(const std::string &aName,
- const std::vector<Ip6Address> &aAddresses,
- ResultCallback &&aCallback)
+Publisher::ResultCallback Publisher::HandleDuplicateHostRegistration(const std::string &aName,
+ const AddressList &aAddresses,
+ ResultCallback &&aCallback)
{
HostRegistration *hostReg = FindHostRegistration(aName);
@@ -431,10 +445,10 @@ bool Publisher::ServiceRegistration::IsOutdated(const std::string &aHostName,
const std::string &aType,
const SubTypeList &aSubTypeList,
uint16_t aPort,
- const TxtList &aTxtList) const
+ const TxtData &aTxtData) const
{
return !(mHostName == aHostName && mName == aName && mType == aType && mSubTypeList == aSubTypeList &&
- mPort == aPort && mTxtList == aTxtList);
+ mPort == aPort && mTxtData == aTxtData);
}
void Publisher::ServiceRegistration::Complete(otbrError aError)
@@ -452,7 +466,7 @@ void Publisher::ServiceRegistration::OnComplete(otbrError aError)
}
}
-bool Publisher::HostRegistration::IsOutdated(const std::string &aName, const std::vector<Ip6Address> &aAddresses) const
+bool Publisher::HostRegistration::IsOutdated(const std::string &aName, const AddressList &aAddresses) const
{
return !(mName == aName && mAddresses == aAddresses);
}
@@ -577,5 +591,4 @@ void Publisher::UpdateHostResolutionEmaLatency(const std::string &aHostName, otb
}
} // namespace Mdns
-
} // namespace otbr
diff --git a/src/mdns/mdns.hpp b/src/mdns/mdns.hpp
index a06578a3..9a28b884 100644
--- a/src/mdns/mdns.hpp
+++ b/src/mdns/mdns.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_AGENT_MDNS_HPP_
#define OTBR_AGENT_MDNS_HPP_
+#include "openthread-br/config.h"
+
#include <functional>
#include <map>
#include <memory>
@@ -68,33 +70,51 @@ class Publisher : private NonCopyable
{
public:
/**
- * This structure represents a name/value pair of the TXT record.
+ * This structure represents a key/value pair of the TXT record.
*
*/
struct TxtEntry
{
- std::string mName; ///< The name of the TXT entry.
- std::vector<uint8_t> mValue; ///< The value of the TXT entry.
+ std::string mKey; ///< The key of the TXT entry.
+ std::vector<uint8_t> mValue; ///< The value of the TXT entry. Can be empty.
+ bool mIsBooleanAttribute; ///< This entry is boolean attribute (encoded as `key` without `=`).
- TxtEntry(const char *aName, const char *aValue)
- : TxtEntry(aName, reinterpret_cast<const uint8_t *>(aValue), strlen(aValue))
+ TxtEntry(const char *aKey, const char *aValue)
+ : TxtEntry(aKey, reinterpret_cast<const uint8_t *>(aValue), strlen(aValue))
{
}
- TxtEntry(const char *aName, const uint8_t *aValue, size_t aValueLength)
- : TxtEntry(aName, strlen(aName), aValue, aValueLength)
+ TxtEntry(const char *aKey, const uint8_t *aValue, size_t aValueLength)
+ : TxtEntry(aKey, strlen(aKey), aValue, aValueLength)
{
}
- TxtEntry(const char *aName, size_t aNameLength, const uint8_t *aValue, size_t aValueLength)
- : mName(aName, aNameLength)
+ TxtEntry(const char *aKey, size_t aKeyLength, const uint8_t *aValue, size_t aValueLength)
+ : mKey(aKey, aKeyLength)
, mValue(aValue, aValue + aValueLength)
+ , mIsBooleanAttribute(false)
+ {
+ }
+
+ TxtEntry(const char *aKey)
+ : TxtEntry(aKey, strlen(aKey))
{
}
- bool operator==(const TxtEntry &aOther) const { return mName == aOther.mName && mValue == aOther.mValue; }
+ TxtEntry(const char *aKey, size_t aKeyLength)
+ : mKey(aKey, aKeyLength)
+ , mIsBooleanAttribute(true)
+ {
+ }
+
+ bool operator==(const TxtEntry &aOther) const
+ {
+ return (mKey == aOther.mKey) && (mValue == aOther.mValue) &&
+ (mIsBooleanAttribute == aOther.mIsBooleanAttribute);
+ }
};
+ typedef std::vector<uint8_t> TxtData;
typedef std::vector<TxtEntry> TxtList;
typedef std::vector<std::string> SubTypeList;
typedef std::vector<Ip6Address> AddressList;
@@ -105,16 +125,16 @@ public:
*/
struct DiscoveredInstanceInfo
{
- bool mRemoved = false; ///< The Service Instance is removed.
- uint32_t mNetifIndex = 0; ///< Network interface.
- std::string mName; ///< Instance name.
- std::string mHostName; ///< Full host name.
- std::vector<Ip6Address> mAddresses; ///< IPv6 addresses.
- uint16_t mPort = 0; ///< Port.
- uint16_t mPriority = 0; ///< Service priority.
- uint16_t mWeight = 0; ///< Service weight.
- std::vector<uint8_t> mTxtData; ///< TXT RDATA bytes.
- uint32_t mTtl = 0; ///< Service TTL.
+ bool mRemoved = false; ///< The Service Instance is removed.
+ uint32_t mNetifIndex = 0; ///< Network interface.
+ std::string mName; ///< Instance name.
+ std::string mHostName; ///< Full host name.
+ AddressList mAddresses; ///< IPv6 addresses.
+ uint16_t mPort = 0; ///< Port.
+ uint16_t mPriority = 0; ///< Service priority.
+ uint16_t mWeight = 0; ///< Service weight.
+ TxtData mTxtData; ///< TXT RDATA bytes.
+ uint32_t mTtl = 0; ///< Service TTL.
};
/**
@@ -123,9 +143,9 @@ public:
*/
struct DiscoveredHostInfo
{
- std::string mHostName; ///< Full host name.
- std::vector<Ip6Address> mAddresses; ///< IP6 addresses.
- uint32_t mTtl = 0; ///< Host TTL.
+ std::string mHostName; ///< Full host name.
+ AddressList mAddresses; ///< IP6 addresses.
+ uint32_t mTtl = 0; ///< Host TTL.
};
/**
@@ -191,10 +211,10 @@ public:
* with method PublishHost.
* @param[in] aName The name of this service. If an empty string is provided, the service's name will be the
* same as the platform's hostname.
- * @param[in] aType The type of this service.
+ * @param[in] aType The type of this service, e.g., "_srv._udp" (MUST NOT end with dot).
* @param[in] aSubTypeList A list of service subtypes.
* @param[in] aPort The port number of this service.
- * @param[in] aTxtList A list of TXT name/value pairs.
+ * @param[in] aTxtData The encoded TXT data for this service.
* @param[in] aCallback The callback for receiving the publishing result. `OTBR_ERROR_NONE` will be
* returned if the operation is successful and all other values indicate a
* failure. Specifically, `OTBR_ERROR_DUPLICATED` indicates that the name has
@@ -207,14 +227,14 @@ public:
const std::string &aType,
const SubTypeList &aSubTypeList,
uint16_t aPort,
- const TxtList &aTxtList,
+ const TxtData &aTxtData,
ResultCallback &&aCallback);
/**
* This method un-publishes a service.
*
* @param[in] aName The name of this service.
- * @param[in] aType The type of this service.
+ * @param[in] aType The type of this service, e.g., "_srv._udp" (MUST NOT end with dot).
* @param[in] aCallback The callback for receiving the publishing result.
*
*/
@@ -235,12 +255,12 @@ public:
* alternative name is available/acceptable.
*
*/
- void PublishHost(const std::string &aName, const std::vector<Ip6Address> &aAddresses, ResultCallback &&aCallback);
+ void PublishHost(const std::string &aName, const AddressList &aAddresses, ResultCallback &&aCallback);
/**
* This method un-publishes a host.
*
- * @param[in] aName A host name.
+ * @param[in] aName A host name (MUST not end with dot).
* @param[in] aCallback The callback for receiving the publishing result.
*
*/
@@ -256,7 +276,7 @@ public:
* @note Discovery Proxy implementation guarantees no duplicate subscriptions for the same service or service
* instance.
*
- * @param[in] aType The service type.
+ * @param[in] aType The service type, e.g., "_srv._udp" (MUST NOT end with dot).
* @param[in] aInstanceName The service instance to subscribe, or empty to subscribe the service.
*
*/
@@ -270,7 +290,7 @@ public:
*
* @note Discovery Proxy implementation guarantees no redundant unsubscription for a service or service instance.
*
- * @param[in] aType The service type.
+ * @param[in] aType The service type, e.g., "_srv._udp" (MUST NOT end with dot).
* @param[in] aInstanceName The service instance to unsubscribe, or empty to unsubscribe the service.
*
*/
@@ -324,7 +344,7 @@ public:
* @returns The MdnsTelemetryInfo of the publisher.
*
*/
- const MdnsTelemetryInfo &GetMdnsTelemetryInfo() const { return mTelemetryInfo; }
+ const MdnsTelemetryInfo &GetMdnsTelemetryInfo(void) const { return mTelemetryInfo; }
virtual ~Publisher(void) = default;
@@ -354,7 +374,7 @@ public:
* See RFC 6763 for details: https://tools.ietf.org/html/rfc6763#section-6.
*
* @param[in] aTxtList A TXT entry list.
- * @param[out] aTxtData A TXT data buffer.
+ * @param[out] aTxtData A TXT data buffer. Will be cleared.
*
* @retval OTBR_ERROR_NONE Successfully write the TXT entry list.
* @retval OTBR_ERROR_INVALID_ARGS The @p aTxtList includes invalid TXT entry.
@@ -362,7 +382,7 @@ public:
* @sa DecodeTxtData
*
*/
- static otbrError EncodeTxtData(const TxtList &aTxtList, std::vector<uint8_t> &aTxtData);
+ static otbrError EncodeTxtData(const TxtList &aTxtList, TxtData &aTxtData);
/**
* This function decodes a TXT entry list from a TXT data buffer.
@@ -422,14 +442,14 @@ protected:
std::string mType;
SubTypeList mSubTypeList;
uint16_t mPort;
- TxtList mTxtList;
+ TxtData mTxtData;
ServiceRegistration(std::string aHostName,
std::string aName,
std::string aType,
SubTypeList aSubTypeList,
uint16_t aPort,
- TxtList aTxtList,
+ TxtData aTxtData,
ResultCallback &&aCallback,
Publisher *aPublisher)
: Registration(std::move(aCallback), aPublisher)
@@ -438,29 +458,30 @@ protected:
, mType(std::move(aType))
, mSubTypeList(SortSubTypeList(std::move(aSubTypeList)))
, mPort(aPort)
- , mTxtList(SortTxtList(std::move(aTxtList)))
+ , mTxtData(std::move(aTxtData))
{
}
~ServiceRegistration(void) override { OnComplete(OTBR_ERROR_ABORTED); }
void Complete(otbrError aError);
- void OnComplete(otbrError aError);
-
// Tells whether this `ServiceRegistration` object is outdated comparing to the given parameters.
bool IsOutdated(const std::string &aHostName,
const std::string &aName,
const std::string &aType,
const SubTypeList &aSubTypeList,
uint16_t aPort,
- const TxtList &aTxtList) const;
+ const TxtData &aTxtData) const;
+
+ private:
+ void OnComplete(otbrError aError);
};
class HostRegistration : public Registration
{
public:
- std::string mName;
- std::vector<Ip6Address> mAddresses;
+ std::string mName;
+ AddressList mAddresses;
HostRegistration(std::string aName, AddressList aAddresses, ResultCallback &&aCallback, Publisher *aPublisher)
: Registration(std::move(aCallback), aPublisher)
@@ -469,14 +490,15 @@ protected:
{
}
- ~HostRegistration(void) { OnComplete(OTBR_ERROR_ABORTED); }
+ ~HostRegistration(void) override { OnComplete(OTBR_ERROR_ABORTED); }
void Complete(otbrError aError);
- void OnComplete(otbrError);
-
// Tells whether this `HostRegistration` object is outdated comparing to the given parameters.
- bool IsOutdated(const std::string &aName, const std::vector<Ip6Address> &aAddresses) const;
+ bool IsOutdated(const std::string &aName, const AddressList &aAddresses) const;
+
+ private:
+ void OnComplete(otbrError aError);
};
using ServiceRegistrationPtr = std::unique_ptr<ServiceRegistration>;
@@ -485,7 +507,6 @@ protected:
using HostRegistrationMap = std::map<std::string, HostRegistrationPtr>;
static SubTypeList SortSubTypeList(SubTypeList aSubTypeList);
- static TxtList SortTxtList(TxtList aTxtList);
static AddressList SortAddressList(AddressList aAddressList);
static std::string MakeFullServiceName(const std::string &aName, const std::string &aType);
static std::string MakeFullHostName(const std::string &aName);
@@ -495,26 +516,30 @@ protected:
const std::string &aType,
const SubTypeList &aSubTypeList,
uint16_t aPort,
- const TxtList &aTxtList,
- ResultCallback &&aCallback) = 0;
- virtual otbrError PublishHostImpl(const std::string &aName,
- const std::vector<Ip6Address> &aAddresses,
- ResultCallback &&aCallback) = 0;
- virtual void OnServiceResolveFailedImpl(const std::string &aType,
- const std::string &aInstanceName,
- int32_t aErrorCode) = 0;
- virtual void OnHostResolveFailedImpl(const std::string &aHostName, int32_t aErrorCode) = 0;
+ const TxtData &aTxtData,
+ ResultCallback &&aCallback) = 0;
+
+ virtual otbrError PublishHostImpl(const std::string &aName,
+ const AddressList &aAddresses,
+ ResultCallback &&aCallback) = 0;
+
+ virtual void OnServiceResolveFailedImpl(const std::string &aType,
+ const std::string &aInstanceName,
+ int32_t aErrorCode) = 0;
+
+ virtual void OnHostResolveFailedImpl(const std::string &aHostName, int32_t aErrorCode) = 0;
virtual otbrError DnsErrorToOtbrError(int32_t aError) = 0;
void AddServiceRegistration(ServiceRegistrationPtr &&aServiceReg);
void RemoveServiceRegistration(const std::string &aName, const std::string &aType, otbrError aError);
ServiceRegistration *FindServiceRegistration(const std::string &aName, const std::string &aType);
- void OnServiceResolved(const std::string &aType, const DiscoveredInstanceInfo &aInstanceInfo);
- void OnServiceResolveFailed(const std::string &aType, const std::string &aInstanceName, int32_t aErrorCode);
- void OnServiceRemoved(uint32_t aNetifIndex, const std::string &aType, const std::string &aInstanceName);
- void OnHostResolved(const std::string &aHostName, const DiscoveredHostInfo &aHostInfo);
- void OnHostResolveFailed(const std::string &aHostName, int32_t aErrorCode);
+
+ void OnServiceResolved(std::string aType, DiscoveredInstanceInfo aInstanceInfo);
+ void OnServiceResolveFailed(std::string aType, std::string aInstanceName, int32_t aErrorCode);
+ void OnServiceRemoved(uint32_t aNetifIndex, std::string aType, std::string aInstanceName);
+ void OnHostResolved(std::string aHostName, DiscoveredHostInfo aHostInfo);
+ void OnHostResolveFailed(std::string aHostName, int32_t aErrorCode);
// Handles the cases that there is already a registration for the same service.
// If the returned callback is completed, current registration should be considered
@@ -524,18 +549,18 @@ protected:
const std::string &aType,
const SubTypeList &aSubTypeList,
uint16_t aPort,
- const TxtList &aTxtList,
+ const TxtData &aTxtData,
ResultCallback &&aCallback);
- ResultCallback HandleDuplicateHostRegistration(const std::string &aName,
- const std::vector<Ip6Address> &aAddresses,
- ResultCallback &&aCallback);
+ ResultCallback HandleDuplicateHostRegistration(const std::string &aName,
+ const AddressList &aAddresses,
+ ResultCallback &&aCallback);
void AddHostRegistration(HostRegistrationPtr &&aHostReg);
void RemoveHostRegistration(const std::string &aName, otbrError aError);
HostRegistration *FindHostRegistration(const std::string &aName);
- static void UpdateMdnsResponseCounters(otbr::MdnsResponseCounters &aCounters, otbrError aError);
+ static void UpdateMdnsResponseCounters(MdnsResponseCounters &aCounters, otbrError aError);
static void UpdateEmaLatency(uint32_t &aEmaLatency, uint32_t aLatency, otbrError aError);
void UpdateServiceRegistrationEmaLatency(const std::string &aInstanceName,
@@ -562,7 +587,7 @@ protected:
// host name -> the timepoint to begin host resolution
std::map<std::string, Timepoint> mHostResolutionBeginTime;
- otbr::MdnsTelemetryInfo mTelemetryInfo{};
+ MdnsTelemetryInfo mTelemetryInfo{};
};
/**
diff --git a/src/mdns/mdns_avahi.cpp b/src/mdns/mdns_avahi.cpp
index cba9081e..898591fb 100644
--- a/src/mdns/mdns_avahi.cpp
+++ b/src/mdns/mdns_avahi.cpp
@@ -53,30 +53,42 @@
#include "common/logging.hpp"
#include "common/time.hpp"
+namespace otbr {
+namespace Mdns {
+
+class AvahiPoller;
+
+} // namespace Mdns
+} // namespace otbr
+
struct AvahiWatch
{
- int mFd; ///< The file descriptor to watch.
- AvahiWatchEvent mEvents; ///< The interested events.
- int mHappened; ///< The events happened.
- AvahiWatchCallback mCallback; ///< The function to be called when interested events happened on mFd.
- void *mContext; ///< A pointer to application-specific context.
- void *mPoller; ///< The poller created this watch.
+ typedef otbr::Mdns::AvahiPoller AvahiPoller;
+
+ int mFd; ///< The file descriptor to watch.
+ AvahiWatchEvent mEvents; ///< The interested events.
+ int mHappened; ///< The events happened.
+ AvahiWatchCallback mCallback; ///< The function to be called to report events happened on `mFd`.
+ void *mContext; ///< A pointer to application-specific context to use with `mCallback`.
+ bool mShouldReport; ///< Whether or not we need to report events (invoking callback).
+ AvahiPoller &mPoller; ///< The poller owning this watch.
/**
* The constructor to initialize an Avahi watch.
*
* @param[in] aFd The file descriptor to watch.
* @param[in] aEvents The events to watch.
- * @param[in] aCallback The function to be called when events happend on this file descriptor.
+ * @param[in] aCallback The function to be called when events happened on this file descriptor.
* @param[in] aContext A pointer to application-specific context.
* @param[in] aPoller The AvahiPoller this watcher belongs to.
*
*/
- AvahiWatch(int aFd, AvahiWatchEvent aEvents, AvahiWatchCallback aCallback, void *aContext, void *aPoller)
+ AvahiWatch(int aFd, AvahiWatchEvent aEvents, AvahiWatchCallback aCallback, void *aContext, AvahiPoller &aPoller)
: mFd(aFd)
, mEvents(aEvents)
, mCallback(aCallback)
, mContext(aContext)
+ , mShouldReport(false)
, mPoller(aPoller)
{
}
@@ -88,10 +100,13 @@ struct AvahiWatch
*/
struct AvahiTimeout
{
- otbr::Timepoint mTimeout; ///< Absolute time when this timer timeout.
- AvahiTimeoutCallback mCallback; ///< The function to be called when timeout.
- void *mContext; ///< The pointer to application-specific context.
- void *mPoller; ///< The poller created this timer.
+ typedef otbr::Mdns::AvahiPoller AvahiPoller;
+
+ otbr::Timepoint mTimeout; ///< Absolute time when this timer timeout.
+ AvahiTimeoutCallback mCallback; ///< The function to be called when timeout.
+ void *mContext; ///< The pointer to application-specific context.
+ bool mShouldReport; ///< Whether or not timeout occurred and need to reported (invoking callback).
+ AvahiPoller &mPoller; ///< The poller created this timer.
/**
* The constructor to initialize an AvahiTimeout.
@@ -102,9 +117,10 @@ struct AvahiTimeout
* @param[in] aPoller The AvahiPoller this timeout belongs to.
*
*/
- AvahiTimeout(const struct timeval *aTimeout, AvahiTimeoutCallback aCallback, void *aContext, void *aPoller)
+ AvahiTimeout(const struct timeval *aTimeout, AvahiTimeoutCallback aCallback, void *aContext, AvahiPoller &aPoller)
: mCallback(aCallback)
, mContext(aContext)
+ , mShouldReport(false)
, mPoller(aPoller)
{
if (aTimeout)
@@ -168,13 +184,13 @@ public:
void Update(MainloopContext &aMainloop) override;
void Process(const MainloopContext &aMainloop) override;
- const AvahiPoll *GetAvahiPoll(void) const { return &mAvahiPoller; }
+ const AvahiPoll *GetAvahiPoll(void) const { return &mAvahiPoll; }
private:
typedef std::vector<AvahiWatch *> Watches;
typedef std::vector<AvahiTimeout *> Timers;
- static AvahiWatch *WatchNew(const struct AvahiPoll *aPoller,
+ static AvahiWatch *WatchNew(const struct AvahiPoll *aPoll,
int aFd,
AvahiWatchEvent aEvent,
AvahiWatchCallback aCallback,
@@ -184,7 +200,7 @@ private:
static AvahiWatchEvent WatchGetEvents(AvahiWatch *aWatch);
static void WatchFree(AvahiWatch *aWatch);
void WatchFree(AvahiWatch &aWatch);
- static AvahiTimeout *TimeoutNew(const AvahiPoll *aPoller,
+ static AvahiTimeout *TimeoutNew(const AvahiPoll *aPoll,
const struct timeval *aTimeout,
AvahiTimeoutCallback aCallback,
void *aContext);
@@ -195,36 +211,36 @@ private:
Watches mWatches;
Timers mTimers;
- AvahiPoll mAvahiPoller;
+ AvahiPoll mAvahiPoll;
};
AvahiPoller::AvahiPoller(void)
{
- mAvahiPoller.userdata = this;
- mAvahiPoller.watch_new = WatchNew;
- mAvahiPoller.watch_update = WatchUpdate;
- mAvahiPoller.watch_get_events = WatchGetEvents;
- mAvahiPoller.watch_free = WatchFree;
-
- mAvahiPoller.timeout_new = TimeoutNew;
- mAvahiPoller.timeout_update = TimeoutUpdate;
- mAvahiPoller.timeout_free = TimeoutFree;
+ mAvahiPoll.userdata = this;
+ mAvahiPoll.watch_new = WatchNew;
+ mAvahiPoll.watch_update = WatchUpdate;
+ mAvahiPoll.watch_get_events = WatchGetEvents;
+ mAvahiPoll.watch_free = WatchFree;
+
+ mAvahiPoll.timeout_new = TimeoutNew;
+ mAvahiPoll.timeout_update = TimeoutUpdate;
+ mAvahiPoll.timeout_free = TimeoutFree;
}
-AvahiWatch *AvahiPoller::WatchNew(const struct AvahiPoll *aPoller,
+AvahiWatch *AvahiPoller::WatchNew(const struct AvahiPoll *aPoll,
int aFd,
AvahiWatchEvent aEvent,
AvahiWatchCallback aCallback,
void *aContext)
{
- return reinterpret_cast<AvahiPoller *>(aPoller->userdata)->WatchNew(aFd, aEvent, aCallback, aContext);
+ return reinterpret_cast<AvahiPoller *>(aPoll->userdata)->WatchNew(aFd, aEvent, aCallback, aContext);
}
AvahiWatch *AvahiPoller::WatchNew(int aFd, AvahiWatchEvent aEvent, AvahiWatchCallback aCallback, void *aContext)
{
assert(aEvent && aCallback && aFd >= 0);
- mWatches.push_back(new AvahiWatch(aFd, aEvent, aCallback, aContext, this));
+ mWatches.push_back(new AvahiWatch(aFd, aEvent, aCallback, aContext, *this));
return mWatches.back();
}
@@ -241,7 +257,7 @@ AvahiWatchEvent AvahiPoller::WatchGetEvents(AvahiWatch *aWatch)
void AvahiPoller::WatchFree(AvahiWatch *aWatch)
{
- reinterpret_cast<AvahiPoller *>(aWatch->mPoller)->WatchFree(*aWatch);
+ aWatch->mPoller.WatchFree(*aWatch);
}
void AvahiPoller::WatchFree(AvahiWatch &aWatch)
@@ -257,18 +273,18 @@ void AvahiPoller::WatchFree(AvahiWatch &aWatch)
}
}
-AvahiTimeout *AvahiPoller::TimeoutNew(const AvahiPoll *aPoller,
+AvahiTimeout *AvahiPoller::TimeoutNew(const AvahiPoll *aPoll,
const struct timeval *aTimeout,
AvahiTimeoutCallback aCallback,
void *aContext)
{
- assert(aPoller && aCallback);
- return static_cast<AvahiPoller *>(aPoller->userdata)->TimeoutNew(aTimeout, aCallback, aContext);
+ assert(aPoll && aCallback);
+ return static_cast<AvahiPoller *>(aPoll->userdata)->TimeoutNew(aTimeout, aCallback, aContext);
}
AvahiTimeout *AvahiPoller::TimeoutNew(const struct timeval *aTimeout, AvahiTimeoutCallback aCallback, void *aContext)
{
- mTimers.push_back(new AvahiTimeout(aTimeout, aCallback, aContext, this));
+ mTimers.push_back(new AvahiTimeout(aTimeout, aCallback, aContext, *this));
return mTimers.back();
}
@@ -286,7 +302,7 @@ void AvahiPoller::TimeoutUpdate(AvahiTimeout *aTimer, const struct timeval *aTim
void AvahiPoller::TimeoutFree(AvahiTimeout *aTimer)
{
- static_cast<AvahiPoller *>(aTimer->mPoller)->TimeoutFree(*aTimer);
+ aTimer->mPoller.TimeoutFree(*aTimer);
}
void AvahiPoller::TimeoutFree(AvahiTimeout &aTimer)
@@ -306,10 +322,10 @@ void AvahiPoller::Update(MainloopContext &aMainloop)
{
Timepoint now = Clock::now();
- for (Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it)
+ for (AvahiWatch *watch : mWatches)
{
- int fd = (*it)->mFd;
- AvahiWatchEvent events = (*it)->mEvents;
+ int fd = watch->mFd;
+ AvahiWatchEvent events = watch->mEvents;
if (AVAHI_WATCH_IN & events)
{
@@ -333,12 +349,12 @@ void AvahiPoller::Update(MainloopContext &aMainloop)
aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, fd);
- (*it)->mHappened = 0;
+ watch->mHappened = 0;
}
- for (Timers::iterator it = mTimers.begin(); it != mTimers.end(); ++it)
+ for (AvahiTimeout *timer : mTimers)
{
- Timepoint timeout = (*it)->mTimeout;
+ Timepoint timeout = timer->mTimeout;
if (timeout == Timepoint::min())
{
@@ -364,56 +380,92 @@ void AvahiPoller::Update(MainloopContext &aMainloop)
void AvahiPoller::Process(const MainloopContext &aMainloop)
{
- Timepoint now = Clock::now();
- std::vector<AvahiTimeout *> expired;
+ Timepoint now = Clock::now();
+ bool shouldReport = false;
- for (Watches::iterator it = mWatches.begin(); it != mWatches.end(); ++it)
+ for (AvahiWatch *watch : mWatches)
{
- int fd = (*it)->mFd;
- AvahiWatchEvent events = (*it)->mEvents;
+ int fd = watch->mFd;
+ AvahiWatchEvent events = watch->mEvents;
- (*it)->mHappened = 0;
+ watch->mHappened = 0;
if ((AVAHI_WATCH_IN & events) && FD_ISSET(fd, &aMainloop.mReadFdSet))
{
- (*it)->mHappened |= AVAHI_WATCH_IN;
+ watch->mHappened |= AVAHI_WATCH_IN;
}
if ((AVAHI_WATCH_OUT & events) && FD_ISSET(fd, &aMainloop.mWriteFdSet))
{
- (*it)->mHappened |= AVAHI_WATCH_OUT;
+ watch->mHappened |= AVAHI_WATCH_OUT;
}
if ((AVAHI_WATCH_ERR & events) && FD_ISSET(fd, &aMainloop.mErrorFdSet))
{
- (*it)->mHappened |= AVAHI_WATCH_ERR;
+ watch->mHappened |= AVAHI_WATCH_ERR;
}
- // TODO hup events
- if ((*it)->mHappened)
+ if (watch->mHappened != 0)
{
- (*it)->mCallback(*it, (*it)->mFd, static_cast<AvahiWatchEvent>((*it)->mHappened), (*it)->mContext);
+ watch->mShouldReport = true;
+ shouldReport = true;
}
}
- for (Timers::iterator it = mTimers.begin(); it != mTimers.end(); ++it)
+ // When we invoke the callback for an `AvahiWatch` or `AvahiTimeout`,
+ // the Avahi module can call any of `mAvahiPoll` APIs we provided to
+ // it. For example, it can update or free any of `AvahiWatch/Timeout`
+ // entries, which in turn, modifies our `mWatches` or `mTimers` list.
+ // So, before invoking the callback, we update the entry's state and
+ // then restart the iteration over the `mWacthes` list to find the
+ // next entry to report, as the list may have changed.
+
+ while (shouldReport)
{
- if ((*it)->mTimeout == Timepoint::min())
+ shouldReport = false;
+
+ for (AvahiWatch *watch : mWatches)
+ {
+ if (watch->mShouldReport)
+ {
+ shouldReport = true;
+ watch->mShouldReport = false;
+ watch->mCallback(watch, watch->mFd, WatchGetEvents(watch), watch->mContext);
+
+ break;
+ }
+ }
+ }
+
+ for (AvahiTimeout *timer : mTimers)
+ {
+ if (timer->mTimeout == Timepoint::min())
{
continue;
}
- if ((*it)->mTimeout <= now)
+ if (timer->mTimeout <= now)
{
- expired.push_back(*it);
+ timer->mShouldReport = true;
+ shouldReport = true;
}
}
- for (std::vector<AvahiTimeout *>::iterator it = expired.begin(); it != expired.end(); ++it)
+ while (shouldReport)
{
- AvahiTimeout *avahiTimeout = *it;
+ shouldReport = false;
+
+ for (AvahiTimeout *timer : mTimers)
+ {
+ if (timer->mShouldReport)
+ {
+ shouldReport = true;
+ timer->mShouldReport = false;
+ timer->mCallback(timer, timer->mContext);
- avahiTimeout->mCallback(avahiTimeout, avahiTimeout->mContext);
+ break;
+ }
+ }
}
}
@@ -639,13 +691,12 @@ otbrError PublisherAvahi::PublishServiceImpl(const std::string &aHostName,
const std::string &aType,
const SubTypeList &aSubTypeList,
uint16_t aPort,
- const TxtList &aTxtList,
+ const TxtData &aTxtData,
ResultCallback &&aCallback)
{
otbrError error = OTBR_ERROR_NONE;
int avahiError = AVAHI_OK;
SubTypeList sortedSubTypeList = SortSubTypeList(aSubTypeList);
- TxtList sortedTxtList = SortTxtList(aTxtList);
const std::string logHostName = !aHostName.empty() ? aHostName : "localhost";
std::string fullHostName;
std::string serviceName = aName;
@@ -667,11 +718,11 @@ otbrError PublisherAvahi::PublishServiceImpl(const std::string &aHostName,
serviceName = avahi_client_get_host_name(mClient);
}
- aCallback = HandleDuplicateServiceRegistration(aHostName, serviceName, aType, sortedSubTypeList, aPort,
- sortedTxtList, std::move(aCallback));
+ aCallback = HandleDuplicateServiceRegistration(aHostName, serviceName, aType, sortedSubTypeList, aPort, aTxtData,
+ std::move(aCallback));
VerifyOrExit(!aCallback.IsNull());
- SuccessOrExit(error = TxtListToAvahiStringList(aTxtList, txtBuffer, sizeof(txtBuffer), txtHead));
+ SuccessOrExit(error = TxtDataToAvahiStringList(aTxtData, txtBuffer, sizeof(txtBuffer), txtHead));
VerifyOrExit((group = CreateGroup(mClient)) != nullptr, error = OTBR_ERROR_MDNS);
avahiError = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AvahiPublishFlags{},
serviceName.c_str(), aType.c_str(),
@@ -693,7 +744,7 @@ otbrError PublisherAvahi::PublishServiceImpl(const std::string &aHostName,
VerifyOrExit(avahiError == AVAHI_OK);
AddServiceRegistration(std::unique_ptr<AvahiServiceRegistration>(new AvahiServiceRegistration(
- aHostName, serviceName, aType, sortedSubTypeList, aPort, sortedTxtList, std::move(aCallback), group, this)));
+ aHostName, serviceName, aType, sortedSubTypeList, aPort, aTxtData, std::move(aCallback), group, this)));
exit:
if (avahiError != AVAHI_OK || error != OTBR_ERROR_NONE)
@@ -724,9 +775,9 @@ exit:
std::move(aCallback)(error);
}
-otbrError PublisherAvahi::PublishHostImpl(const std::string &aName,
- const std::vector<Ip6Address> &aAddresses,
- ResultCallback &&aCallback)
+otbrError PublisherAvahi::PublishHostImpl(const std::string &aName,
+ const AddressList &aAddresses,
+ ResultCallback &&aCallback)
{
otbrError error = OTBR_ERROR_NONE;
int avahiError = AVAHI_OK;
@@ -790,7 +841,7 @@ exit:
std::move(aCallback)(error);
}
-otbrError PublisherAvahi::TxtListToAvahiStringList(const TxtList &aTxtList,
+otbrError PublisherAvahi::TxtDataToAvahiStringList(const TxtData &aTxtData,
AvahiStringList *aBuffer,
size_t aBufferSize,
AvahiStringList *&aHead)
@@ -799,32 +850,40 @@ otbrError PublisherAvahi::TxtListToAvahiStringList(const TxtList &aTxtList,
size_t used = 0;
AvahiStringList *last = nullptr;
AvahiStringList *curr = aBuffer;
+ const uint8_t *next;
+ const uint8_t *data = aTxtData.data();
+ const uint8_t *dataEnd = aTxtData.data() + aTxtData.size();
aHead = nullptr;
- for (const auto &txtEntry : aTxtList)
+
+ while (data < dataEnd)
{
- const char *name = txtEntry.mName.c_str();
- size_t nameLength = txtEntry.mName.length();
- const uint8_t *value = txtEntry.mValue.data();
- size_t valueLength = txtEntry.mValue.size();
- // +1 for the size of "=", avahi doesn't need '\0' at the end of the entry
- size_t needed = sizeof(AvahiStringList) - sizeof(AvahiStringList::text) + nameLength + valueLength + 1;
+ uint8_t entryLength = *data++;
+ size_t needed = sizeof(AvahiStringList) - sizeof(AvahiStringList::text) + entryLength;
+
+ if (entryLength == 0)
+ {
+ continue;
+ }
+
+ VerifyOrExit(data + entryLength <= dataEnd, error = OTBR_ERROR_PARSE);
VerifyOrExit(used + needed <= aBufferSize, error = OTBR_ERROR_INVALID_ARGS);
curr->next = last;
last = curr;
- memcpy(curr->text, name, nameLength);
- curr->text[nameLength] = '=';
- memcpy(curr->text + nameLength + 1, value, valueLength);
- curr->size = nameLength + valueLength + 1;
- {
- const uint8_t *next = curr->text + curr->size;
- curr = OTBR_ALIGNED(next, AvahiStringList *);
- }
+
+ memcpy(curr->text, data, entryLength);
+ curr->size = entryLength;
+
+ data += entryLength;
+
+ next = curr->text + curr->size;
+ curr = OTBR_ALIGNED(next, AvahiStringList *);
used = static_cast<size_t>(reinterpret_cast<uint8_t *>(curr) - reinterpret_cast<uint8_t *>(aBuffer));
}
- SuccessOrExit(error);
+
aHead = last;
+
exit:
return error;
}
@@ -1071,18 +1130,22 @@ void PublisherAvahi::ServiceSubscription::Resolve(uint32_t aInterfaceI
const std::string &aInstanceName,
const std::string &aType)
{
- AvahiServiceResolver *resolver;
+ auto serviceResolver = MakeUnique<ServiceResolver>();
mPublisherAvahi->mServiceInstanceResolutionBeginTime[std::make_pair(aInstanceName, aType)] = Clock::now();
otbrLogInfo("Resolve service %s.%s inf %" PRIu32, aInstanceName.c_str(), aType.c_str(), aInterfaceIndex);
- resolver = avahi_service_resolver_new(
+ serviceResolver->mType = aType;
+ serviceResolver->mPublisherAvahi = this->mPublisherAvahi;
+ serviceResolver->mServiceResolver = avahi_service_resolver_new(
mPublisherAvahi->mClient, aInterfaceIndex, aProtocol, aInstanceName.c_str(), aType.c_str(),
- /* domain */ nullptr, AVAHI_PROTO_UNSPEC, static_cast<AvahiLookupFlags>(0), HandleResolveResult, this);
- if (resolver != nullptr)
+ /* domain */ nullptr, AVAHI_PROTO_UNSPEC, static_cast<AvahiLookupFlags>(AVAHI_LOOKUP_NO_ADDRESS),
+ &ServiceResolver::HandleResolveServiceResult, serviceResolver.get());
+
+ if (serviceResolver->mServiceResolver != nullptr)
{
- AddServiceResolver(aInstanceName, resolver);
+ AddServiceResolver(aInstanceName, serviceResolver.release());
}
else
{
@@ -1091,106 +1154,179 @@ void PublisherAvahi::ServiceSubscription::Resolve(uint32_t aInterfaceI
}
}
-void PublisherAvahi::ServiceSubscription::HandleResolveResult(AvahiServiceResolver *aServiceResolver,
- AvahiIfIndex aInterfaceIndex,
- AvahiProtocol aProtocol,
- AvahiResolverEvent aEvent,
- const char *aName,
- const char *aType,
- const char *aDomain,
- const char *aHostName,
- const AvahiAddress *aAddress,
- uint16_t aPort,
- AvahiStringList *aTxt,
- AvahiLookupResultFlags aFlags,
- void *aContext)
+void PublisherAvahi::ServiceResolver::HandleResolveServiceResult(AvahiServiceResolver *aServiceResolver,
+ AvahiIfIndex aInterfaceIndex,
+ AvahiProtocol aProtocol,
+ AvahiResolverEvent aEvent,
+ const char *aName,
+ const char *aType,
+ const char *aDomain,
+ const char *aHostName,
+ const AvahiAddress *aAddress,
+ uint16_t aPort,
+ AvahiStringList *aTxt,
+ AvahiLookupResultFlags aFlags,
+ void *aContext)
{
- static_cast<PublisherAvahi::ServiceSubscription *>(aContext)->HandleResolveResult(
+ static_cast<PublisherAvahi::ServiceResolver *>(aContext)->HandleResolveServiceResult(
aServiceResolver, aInterfaceIndex, aProtocol, aEvent, aName, aType, aDomain, aHostName, aAddress, aPort, aTxt,
aFlags);
}
-void PublisherAvahi::ServiceSubscription::HandleResolveResult(AvahiServiceResolver *aServiceResolver,
- AvahiIfIndex aInterfaceIndex,
- AvahiProtocol aProtocol,
- AvahiResolverEvent aEvent,
- const char *aName,
- const char *aType,
- const char *aDomain,
- const char *aHostName,
- const AvahiAddress *aAddress,
- uint16_t aPort,
- AvahiStringList *aTxt,
- AvahiLookupResultFlags aFlags)
+void PublisherAvahi::ServiceResolver::HandleResolveServiceResult(AvahiServiceResolver *aServiceResolver,
+ AvahiIfIndex aInterfaceIndex,
+ AvahiProtocol aProtocol,
+ AvahiResolverEvent aEvent,
+ const char *aName,
+ const char *aType,
+ const char *aDomain,
+ const char *aHostName,
+ const AvahiAddress *aAddress,
+ uint16_t aPort,
+ AvahiStringList *aTxt,
+ AvahiLookupResultFlags aFlags)
{
OT_UNUSED_VARIABLE(aServiceResolver);
OT_UNUSED_VARIABLE(aInterfaceIndex);
OT_UNUSED_VARIABLE(aProtocol);
OT_UNUSED_VARIABLE(aType);
OT_UNUSED_VARIABLE(aDomain);
+ OT_UNUSED_VARIABLE(aAddress);
- char addrBuf[AVAHI_ADDRESS_STR_MAX] = "";
- Ip6Address address;
- size_t totalTxtSize = 0;
- DiscoveredInstanceInfo instanceInfo;
- bool resolved = false;
- int avahiError = AVAHI_OK;
+ size_t totalTxtSize = 0;
+ bool resolved = false;
+ int avahiError = AVAHI_OK;
otbrLog(aEvent == AVAHI_RESOLVER_FOUND ? OTBR_LOG_INFO : OTBR_LOG_WARNING, OTBR_LOG_TAG,
"Resolve service reply: protocol %d %s.%s.%s = host %s port %" PRIu16 " flags %d event %d", aProtocol,
aName, aType, aDomain, aHostName, aPort, static_cast<int>(aFlags), static_cast<int>(aEvent));
VerifyOrExit(aEvent == AVAHI_RESOLVER_FOUND, avahiError = avahi_client_errno(mPublisherAvahi->mClient));
-
- avahi_address_snprint(addrBuf, sizeof(addrBuf), aAddress);
- otbrLogInfo("Resolve service reply: address %s", addrBuf);
-
VerifyOrExit(aHostName != nullptr, avahiError = AVAHI_ERR_INVALID_HOST_NAME);
- instanceInfo.mNetifIndex = static_cast<uint32_t>(aInterfaceIndex);
- instanceInfo.mName = aName;
- instanceInfo.mHostName = std::string(aHostName) + ".";
- instanceInfo.mPort = aPort;
- VerifyOrExit(otbrError::OTBR_ERROR_NONE == Ip6Address::FromString(addrBuf, address),
- otbrLogErr("Failed to parse the IP address: %s", addrBuf), avahiError = AVAHI_ERR_INVALID_ADDRESS);
+ mInstanceInfo.mNetifIndex = static_cast<uint32_t>(aInterfaceIndex);
+ mInstanceInfo.mName = aName;
+ mInstanceInfo.mHostName = std::string(aHostName) + ".";
+ mInstanceInfo.mPort = aPort;
otbrLogInfo("Resolve service reply: flags=%u, host=%s", aFlags, aHostName);
- VerifyOrExit(!address.IsLinkLocal() && !address.IsMulticast() && !address.IsLoopback() && !address.IsUnspecified(),
- otbrLogInfo("Ignoring address %s", address.ToString().c_str()),
- avahiError = AVAHI_ERR_INVALID_ADDRESS);
-
- instanceInfo.mAddresses.push_back(address);
-
// TODO priority
// TODO weight
// TODO use a more proper TTL
- instanceInfo.mTtl = kDefaultTtl;
+ mInstanceInfo.mTtl = kDefaultTtl;
for (auto p = aTxt; p; p = avahi_string_list_get_next(p))
{
totalTxtSize += avahi_string_list_get_size(p) + 1;
}
- instanceInfo.mTxtData.resize(totalTxtSize);
- avahi_string_list_serialize(aTxt, instanceInfo.mTxtData.data(), totalTxtSize);
+ mInstanceInfo.mTxtData.resize(totalTxtSize);
+ avahi_string_list_serialize(aTxt, mInstanceInfo.mTxtData.data(), totalTxtSize);
+
+ // NOTE: Avahi only returns one of the host's addresses in the service resolution callback. However, the address may
+ // be link-local so it may not be preferred from Thread's perspective. We want to go through the complete list of
+ // addresses associated with the host and choose a routable address. Therefore, as below we will resolve the host
+ // and go through all its addresses.
+
+ resolved = true;
+
+exit:
+ if (resolved)
+ {
+ // In case the callback is triggered when a service instance is updated, there may already be a record browser.
+ // We should free it before switching to the new record browser.
+ if (mRecordBrowser)
+ {
+ avahi_record_browser_free(mRecordBrowser);
+ mRecordBrowser = nullptr;
+ }
+ // NOTE: This `ServiceResolver` object may be freed in `OnServiceResolved`.
+ mRecordBrowser = avahi_record_browser_new(mPublisherAvahi->mClient, aInterfaceIndex, AVAHI_PROTO_UNSPEC,
+ aHostName, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA,
+ static_cast<AvahiLookupFlags>(0), HandleResolveHostResult, this);
+ if (!mRecordBrowser)
+ {
+ resolved = false;
+ avahiError = avahi_client_errno(mPublisherAvahi->mClient);
+ }
+ }
+ if (!resolved && avahiError != AVAHI_OK)
+ {
+ mPublisherAvahi->OnServiceResolveFailed(aType, aName, avahiError);
+ }
+}
+
+void PublisherAvahi::ServiceResolver::HandleResolveHostResult(AvahiRecordBrowser *aRecordBrowser,
+ AvahiIfIndex aInterfaceIndex,
+ AvahiProtocol aProtocol,
+ AvahiBrowserEvent aEvent,
+ const char *aName,
+ uint16_t aClazz,
+ uint16_t aType,
+ const void *aRdata,
+ size_t aSize,
+ AvahiLookupResultFlags aFlags,
+ void *aContext)
+{
+ static_cast<PublisherAvahi::ServiceResolver *>(aContext)->HandleResolveHostResult(
+ aRecordBrowser, aInterfaceIndex, aProtocol, aEvent, aName, aClazz, aType, aRdata, aSize, aFlags);
+}
+
+void PublisherAvahi::ServiceResolver::HandleResolveHostResult(AvahiRecordBrowser *aRecordBrowser,
+ AvahiIfIndex aInterfaceIndex,
+ AvahiProtocol aProtocol,
+ AvahiBrowserEvent aEvent,
+ const char *aName,
+ uint16_t aClazz,
+ uint16_t aType,
+ const void *aRdata,
+ size_t aSize,
+ AvahiLookupResultFlags aFlags)
+{
+ OTBR_UNUSED_VARIABLE(aRecordBrowser);
+ OTBR_UNUSED_VARIABLE(aInterfaceIndex);
+ OTBR_UNUSED_VARIABLE(aProtocol);
+ OTBR_UNUSED_VARIABLE(aEvent);
+ OTBR_UNUSED_VARIABLE(aClazz);
+ OTBR_UNUSED_VARIABLE(aType);
+ OTBR_UNUSED_VARIABLE(aFlags);
- otbrLogInfo("Resolve service reply: address=%s, ttl=%" PRIu32, address.ToString().c_str(), instanceInfo.mTtl);
+ Ip6Address address;
+ bool resolved = false;
+ int avahiError = AVAHI_OK;
+ otbrLog(aEvent != AVAHI_BROWSER_FAILURE ? OTBR_LOG_INFO : OTBR_LOG_WARNING, OTBR_LOG_TAG,
+ "Resolve host reply: %s inf %d protocol %d class %" PRIu16 " type %" PRIu16 " size %zu flags %d event %d",
+ aName, aInterfaceIndex, aProtocol, aClazz, aType, aSize, static_cast<int>(aFlags),
+ static_cast<int>(aEvent));
+
+ VerifyOrExit(aEvent == AVAHI_BROWSER_NEW);
+ VerifyOrExit(aSize == OTBR_IP6_ADDRESS_SIZE || aSize == OTBR_IP4_ADDRESS_SIZE,
+ otbrLogErr("Unexpected address data length: %zu", aSize), avahiError = AVAHI_ERR_INVALID_ADDRESS);
+ VerifyOrExit(aSize == OTBR_IP6_ADDRESS_SIZE, otbrLogInfo("IPv4 address ignored"),
+ avahiError = AVAHI_ERR_INVALID_ADDRESS);
+ address = Ip6Address(*static_cast<const uint8_t(*)[OTBR_IP6_ADDRESS_SIZE]>(aRdata));
+
+ VerifyOrExit(!address.IsLinkLocal() && !address.IsMulticast() && !address.IsLoopback() && !address.IsUnspecified(),
+ avahiError = AVAHI_ERR_INVALID_ADDRESS);
+ otbrLogInfo("Resolved host address: %s", address.ToString().c_str());
+
+ mInstanceInfo.mAddresses.push_back(std::move(address));
resolved = true;
exit:
if (resolved)
{
- // NOTE: This `ServiceSubscrption` object may be freed in `OnServiceResolved`.
- mPublisherAvahi->OnServiceResolved(mType, instanceInfo);
+ // NOTE: This `HostSubscrption` object may be freed in `OnHostResolved`.
+ mPublisherAvahi->OnServiceResolved(mType, mInstanceInfo);
}
else if (avahiError != AVAHI_OK)
{
- mPublisherAvahi->OnServiceResolveFailed(aType, aName, avahiError);
+ mPublisherAvahi->OnServiceResolveFailed(mType, mInstanceInfo.mName, avahiError);
}
}
-void PublisherAvahi::ServiceSubscription::AddServiceResolver(const std::string &aInstanceName,
- AvahiServiceResolver *aServiceResolver)
+void PublisherAvahi::ServiceSubscription::AddServiceResolver(const std::string &aInstanceName,
+ ServiceResolver *aServiceResolver)
{
assert(aServiceResolver != nullptr);
mServiceResolvers[aInstanceName].insert(aServiceResolver);
@@ -1208,7 +1344,7 @@ void PublisherAvahi::ServiceSubscription::RemoveServiceResolver(const std::strin
for (auto resolver : mServiceResolvers[aInstanceName])
{
- avahi_service_resolver_free(resolver);
+ delete resolver;
}
mServiceResolvers.erase(aInstanceName);
diff --git a/src/mdns/mdns_avahi.hpp b/src/mdns/mdns_avahi.hpp
index 1704f763..844e2429 100644
--- a/src/mdns/mdns_avahi.hpp
+++ b/src/mdns/mdns_avahi.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_AGENT_MDNS_AVAHI_HPP_
#define OTBR_AGENT_MDNS_AVAHI_HPP_
+#include "openthread-br/config.h"
+
#include <memory>
#include <set>
#include <vector>
@@ -90,11 +92,11 @@ protected:
const std::string &aType,
const SubTypeList &aSubTypeList,
uint16_t aPort,
- const TxtList &aTxtList,
+ const TxtData &aTxtData,
ResultCallback &&aCallback) override;
- otbrError PublishHostImpl(const std::string &aName,
- const std::vector<Ip6Address> &aAddresses,
- ResultCallback &&aCallback) override;
+ otbrError PublishHostImpl(const std::string &aName,
+ const AddressList &aAddresses,
+ ResultCallback &&aCallback) override;
void OnServiceResolveFailedImpl(const std::string &aType,
const std::string &aInstanceName,
int32_t aErrorCode) override;
@@ -113,7 +115,7 @@ private:
const std::string &aType,
const SubTypeList &aSubTypeList,
uint16_t aPort,
- const TxtList &aTxtList,
+ const TxtData &aTxtData,
ResultCallback &&aCallback,
AvahiEntryGroup *aEntryGroup,
PublisherAvahi *aPublisher)
@@ -122,7 +124,7 @@ private:
aType,
aSubTypeList,
aPort,
- aTxtList,
+ aTxtData,
std::move(aCallback),
aPublisher)
, mEntryGroup(aEntryGroup)
@@ -139,11 +141,11 @@ private:
class AvahiHostRegistration : public HostRegistration
{
public:
- AvahiHostRegistration(const std::string &aName,
- const std::vector<Ip6Address> &aAddresses,
- ResultCallback &&aCallback,
- AvahiEntryGroup *aEntryGroup,
- PublisherAvahi *aPublisher)
+ AvahiHostRegistration(const std::string &aName,
+ const AddressList &aAddresses,
+ ResultCallback &&aCallback,
+ AvahiEntryGroup *aEntryGroup,
+ PublisherAvahi *aPublisher)
: HostRegistration(aName, aAddresses, std::move(aCallback), aPublisher)
, mEntryGroup(aEntryGroup)
{
@@ -166,6 +168,117 @@ private:
}
};
+ struct HostSubscription : public Subscription
+ {
+ explicit HostSubscription(PublisherAvahi &aAvahiPublisher, std::string aHostName)
+ : Subscription(aAvahiPublisher)
+ , mHostName(std::move(aHostName))
+ , mRecordBrowser(nullptr)
+ {
+ }
+
+ ~HostSubscription() { Release(); }
+
+ void Release(void);
+ void Resolve(void);
+ static void HandleResolveResult(AvahiRecordBrowser *aRecordBrowser,
+ AvahiIfIndex aInterfaceIndex,
+ AvahiProtocol aProtocol,
+ AvahiBrowserEvent aEvent,
+ const char *aName,
+ uint16_t aClazz,
+ uint16_t aType,
+ const void *aRdata,
+ size_t aSize,
+ AvahiLookupResultFlags aFlags,
+ void *aContext);
+
+ void HandleResolveResult(AvahiRecordBrowser *aRecordBrowser,
+ AvahiIfIndex aInterfaceIndex,
+ AvahiProtocol aProtocol,
+ AvahiBrowserEvent aEvent,
+ const char *aName,
+ uint16_t aClazz,
+ uint16_t aType,
+ const void *aRdata,
+ size_t aSize,
+ AvahiLookupResultFlags aFlags);
+
+ std::string mHostName;
+ DiscoveredHostInfo mHostInfo;
+ AvahiRecordBrowser *mRecordBrowser;
+ };
+
+ struct ServiceResolver
+ {
+ ~ServiceResolver()
+ {
+ if (mServiceResolver)
+ {
+ avahi_service_resolver_free(mServiceResolver);
+ }
+ if (mRecordBrowser)
+ {
+ avahi_record_browser_free(mRecordBrowser);
+ }
+ }
+
+ static void HandleResolveServiceResult(AvahiServiceResolver *aServiceResolver,
+ AvahiIfIndex aInterfaceIndex,
+ AvahiProtocol Protocol,
+ AvahiResolverEvent aEvent,
+ const char *aName,
+ const char *aType,
+ const char *aDomain,
+ const char *aHostName,
+ const AvahiAddress *aAddress,
+ uint16_t aPort,
+ AvahiStringList *aTxt,
+ AvahiLookupResultFlags aFlags,
+ void *aContext);
+
+ void HandleResolveServiceResult(AvahiServiceResolver *aServiceResolver,
+ AvahiIfIndex aInterfaceIndex,
+ AvahiProtocol Protocol,
+ AvahiResolverEvent aEvent,
+ const char *aName,
+ const char *aType,
+ const char *aDomain,
+ const char *aHostName,
+ const AvahiAddress *aAddress,
+ uint16_t aPort,
+ AvahiStringList *aTxt,
+ AvahiLookupResultFlags aFlags);
+
+ static void HandleResolveHostResult(AvahiRecordBrowser *aRecordBrowser,
+ AvahiIfIndex aInterfaceIndex,
+ AvahiProtocol aProtocol,
+ AvahiBrowserEvent aEvent,
+ const char *aName,
+ uint16_t aClazz,
+ uint16_t aType,
+ const void *aRdata,
+ size_t aSize,
+ AvahiLookupResultFlags aFlags,
+ void *aContext);
+
+ void HandleResolveHostResult(AvahiRecordBrowser *aRecordBrowser,
+ AvahiIfIndex aInterfaceIndex,
+ AvahiProtocol aProtocol,
+ AvahiBrowserEvent aEvent,
+ const char *aName,
+ uint16_t aClazz,
+ uint16_t aType,
+ const void *aRdata,
+ size_t aSize,
+ AvahiLookupResultFlags aFlags);
+
+ std::string mType;
+ PublisherAvahi *mPublisherAvahi;
+ AvahiServiceResolver *mServiceResolver = nullptr;
+ AvahiRecordBrowser *mRecordBrowser = nullptr;
+ DiscoveredInstanceInfo mInstanceInfo;
+ };
struct ServiceSubscription : public Subscription
{
explicit ServiceSubscription(PublisherAvahi &aPublisherAvahi, std::string aType, std::string aInstanceName)
@@ -184,7 +297,7 @@ private:
AvahiProtocol aProtocol,
const std::string &aInstanceName,
const std::string &aType);
- void AddServiceResolver(const std::string &aInstanceName, AvahiServiceResolver *aServiceResolver);
+ void AddServiceResolver(const std::string &aInstanceName, ServiceResolver *aServiceResolver);
void RemoveServiceResolver(const std::string &aInstanceName);
static void HandleBrowseResult(AvahiServiceBrowser *aServiceBrowser,
@@ -206,82 +319,14 @@ private:
const char *aDomain,
AvahiLookupResultFlags aFlags);
- static void HandleResolveResult(AvahiServiceResolver *aServiceResolver,
- AvahiIfIndex aInterfaceIndex,
- AvahiProtocol Protocol,
- AvahiResolverEvent aEvent,
- const char *aName,
- const char *aType,
- const char *aDomain,
- const char *aHostName,
- const AvahiAddress *aAddress,
- uint16_t aPort,
- AvahiStringList *aTxt,
- AvahiLookupResultFlags aFlags,
- void *aContext);
-
- void HandleResolveResult(AvahiServiceResolver *aServiceResolver,
- AvahiIfIndex aInterfaceIndex,
- AvahiProtocol Protocol,
- AvahiResolverEvent aEvent,
- const char *aName,
- const char *aType,
- const char *aDomain,
- const char *aHostName,
- const AvahiAddress *aAddress,
- uint16_t aPort,
- AvahiStringList *aTxt,
- AvahiLookupResultFlags aFlags);
-
std::string mType;
std::string mInstanceName;
AvahiServiceBrowser *mServiceBrowser;
- using ServiceResolversMap = std::map<std::string, std::set<AvahiServiceResolver *>>;
+ using ServiceResolversMap = std::map<std::string, std::set<ServiceResolver *>>;
ServiceResolversMap mServiceResolvers;
};
- struct HostSubscription : public Subscription
- {
- explicit HostSubscription(PublisherAvahi &aAvahiPublisher, std::string aHostName)
- : Subscription(aAvahiPublisher)
- , mHostName(std::move(aHostName))
- , mRecordBrowser(nullptr)
- {
- }
-
- ~HostSubscription() { Release(); }
-
- void Release(void);
- void Resolve(void);
- static void HandleResolveResult(AvahiRecordBrowser *aRecordBrowser,
- AvahiIfIndex aInterfaceIndex,
- AvahiProtocol aProtocol,
- AvahiBrowserEvent aEvent,
- const char *aName,
- uint16_t aClazz,
- uint16_t aType,
- const void *aRdata,
- size_t aSize,
- AvahiLookupResultFlags aFlags,
- void *aContext);
-
- void HandleResolveResult(AvahiRecordBrowser *aRecordBrowser,
- AvahiIfIndex aInterfaceIndex,
- AvahiProtocol aProtocol,
- AvahiBrowserEvent aEvent,
- const char *aName,
- uint16_t aClazz,
- uint16_t aType,
- const void *aRdata,
- size_t aSize,
- AvahiLookupResultFlags aFlags);
-
- std::string mHostName;
- DiscoveredHostInfo mHostInfo;
- AvahiRecordBrowser *mRecordBrowser;
- };
-
typedef std::vector<std::unique_ptr<ServiceSubscription>> ServiceSubscriptionList;
typedef std::vector<std::unique_ptr<HostSubscription>> HostSubscriptionList;
@@ -295,7 +340,7 @@ private:
void HandleGroupState(AvahiEntryGroup *aGroup, AvahiEntryGroupState aState);
void CallHostOrServiceCallback(AvahiEntryGroup *aGroup, otbrError aError);
- static otbrError TxtListToAvahiStringList(const TxtList &aTxtList,
+ static otbrError TxtDataToAvahiStringList(const TxtData &aTxtData,
AvahiStringList *aBuffer,
size_t aBufferSize,
AvahiStringList *&aHead);
diff --git a/src/mdns/mdns_mdnssd.cpp b/src/mdns/mdns_mdnssd.cpp
index f95d2f50..5a07559f 100644
--- a/src/mdns/mdns_mdnssd.cpp
+++ b/src/mdns/mdns_mdnssd.cpp
@@ -222,7 +222,7 @@ PublisherMDnsSd::PublisherMDnsSd(StateCallback aCallback)
PublisherMDnsSd::~PublisherMDnsSd(void)
{
- Stop();
+ Stop(kNormalStop);
}
otbrError PublisherMDnsSd::Start(void)
@@ -237,25 +237,31 @@ bool PublisherMDnsSd::IsStarted(void) const
return mState == State::kReady;
}
-void PublisherMDnsSd::Stop(void)
+void PublisherMDnsSd::Stop(StopMode aStopMode)
{
- ServiceRegistrationMap serviceRegistrations;
- HostRegistrationMap hostRegistrations;
-
VerifyOrExit(mState == State::kReady);
- std::swap(mServiceRegistrations, serviceRegistrations);
- std::swap(mHostRegistrations, hostRegistrations);
+ // If we get a `kDNSServiceErr_ServiceNotRunning` and need to
+ // restart the `Publisher`, we should immediately de-allocate
+ // all `ServiceRef`. Otherwise, we first clear the `Registrations`
+ // list so that `DnssdHostRegisteration` destructor gets the chance
+ // to update registered records if needed.
- if (mHostsRef != nullptr)
+ switch (aStopMode)
{
- DNSServiceRefDeallocate(mHostsRef);
- otbrLogDebug("Deallocated DNSServiceRef for hosts: %p", mHostsRef);
- mHostsRef = nullptr;
+ case kNormalStop:
+ break;
+
+ case kStopOnServiceNotRunningError:
+ DeallocateHostsRef();
+ break;
}
- mSubscribedServices.clear();
+ mServiceRegistrations.clear();
+ mHostRegistrations.clear();
+ DeallocateHostsRef();
+ mSubscribedServices.clear();
mSubscribedHosts.clear();
mState = State::kIdle;
@@ -264,21 +270,39 @@ exit:
return;
}
+DNSServiceErrorType PublisherMDnsSd::CreateSharedHostsRef(void)
+{
+ DNSServiceErrorType dnsError = kDNSServiceErr_NoError;
+
+ VerifyOrExit(mHostsRef == nullptr);
+
+ dnsError = DNSServiceCreateConnection(&mHostsRef);
+ otbrLogDebug("Created new shared DNSServiceRef: %p", mHostsRef);
+
+exit:
+ return dnsError;
+}
+
+void PublisherMDnsSd::DeallocateHostsRef(void)
+{
+ VerifyOrExit(mHostsRef != nullptr);
+
+ HandleServiceRefDeallocating(mHostsRef);
+ DNSServiceRefDeallocate(mHostsRef);
+ otbrLogDebug("Deallocated DNSServiceRef for hosts: %p", mHostsRef);
+ mHostsRef = nullptr;
+
+exit:
+ return;
+}
+
void PublisherMDnsSd::Update(MainloopContext &aMainloop)
{
for (auto &kv : mServiceRegistrations)
{
auto &serviceReg = static_cast<DnssdServiceRegistration &>(*kv.second);
- assert(serviceReg.GetServiceRef() != nullptr);
-
- int fd = DNSServiceRefSockFD(serviceReg.GetServiceRef());
-
- if (fd != -1)
- {
- FD_SET(fd, &aMainloop.mReadFdSet);
- aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, fd);
- }
+ serviceReg.Update(aMainloop);
}
if (mHostsRef != nullptr)
@@ -305,17 +329,13 @@ void PublisherMDnsSd::Update(MainloopContext &aMainloop)
void PublisherMDnsSd::Process(const MainloopContext &aMainloop)
{
- std::vector<DNSServiceRef> readyServices;
+ mServiceRefsToProcess.clear();
for (auto &kv : mServiceRegistrations)
{
auto &serviceReg = static_cast<DnssdServiceRegistration &>(*kv.second);
- int fd = DNSServiceRefSockFD(serviceReg.GetServiceRef());
- if (FD_ISSET(fd, &aMainloop.mReadFdSet))
- {
- readyServices.push_back(serviceReg.GetServiceRef());
- }
+ serviceReg.Process(aMainloop, mServiceRefsToProcess);
}
if (mHostsRef != nullptr)
@@ -324,23 +344,41 @@ void PublisherMDnsSd::Process(const MainloopContext &aMainloop)
if (FD_ISSET(fd, &aMainloop.mReadFdSet))
{
- readyServices.push_back(mHostsRef);
+ mServiceRefsToProcess.push_back(mHostsRef);
}
}
for (const auto &service : mSubscribedServices)
{
- service->ProcessAll(aMainloop, readyServices);
+ service->ProcessAll(aMainloop, mServiceRefsToProcess);
}
for (const auto &host : mSubscribedHosts)
{
- host->Process(aMainloop, readyServices);
+ host->Process(aMainloop, mServiceRefsToProcess);
}
- for (DNSServiceRef serviceRef : readyServices)
+ for (DNSServiceRef serviceRef : mServiceRefsToProcess)
{
- DNSServiceErrorType error = DNSServiceProcessResult(serviceRef);
+ DNSServiceErrorType error;
+
+ // As we go through the list of `mServiceRefsToProcess` the call
+ // to `DNSServiceProcessResult()` can itself invoke callbacks
+ // into `PublisherMDnsSd` and OT, which in turn, may change the
+ // state of `Publisher` and potentially trigger a previously
+ // valid `ServiceRef` in the list to be deallocated. We use
+ // `HandleServiceRefDeallocating()` which is called whenever a
+ // `ServiceRef` is being deallocated and from this we update
+ // the entry in `mServiceRefsToProcess` list to `nullptr` so to
+ // avoid calling `DNSServiceProcessResult()` on an already
+ // freed `ServiceRef`.
+
+ if (serviceRef == nullptr)
+ {
+ continue;
+ }
+
+ error = DNSServiceProcessResult(serviceRef);
if (error != kDNSServiceErr_NoError)
{
@@ -351,7 +389,7 @@ void PublisherMDnsSd::Process(const MainloopContext &aMainloop)
if (error == kDNSServiceErr_ServiceNotRunning)
{
otbrLogWarning("Need to reconnect to mdnsd");
- Stop();
+ Stop(kStopOnServiceNotRunningError);
Start();
ExitNow();
}
@@ -360,129 +398,235 @@ exit:
return;
}
-PublisherMDnsSd::DnssdServiceRegistration::~DnssdServiceRegistration(void)
+void PublisherMDnsSd::HandleServiceRefDeallocating(const DNSServiceRef &aServiceRef)
{
- if (mServiceRef != nullptr)
+ for (DNSServiceRef &entry : mServiceRefsToProcess)
{
- DNSServiceRefDeallocate(mServiceRef);
+ if (entry == aServiceRef)
+ {
+ entry = nullptr;
+ }
}
}
-PublisherMDnsSd::DnssdHostRegistration::~DnssdHostRegistration(void)
+void PublisherMDnsSd::DnssdServiceRegistration::Update(MainloopContext &aMainloop) const
{
- int dnsError;
+ int fd;
VerifyOrExit(mServiceRef != nullptr);
- for (const auto &recordRefAndAddress : GetRecordRefMap())
- {
- const DNSRecordRef &recordRef = recordRefAndAddress.first;
- const Ip6Address &address = recordRefAndAddress.second;
- if (IsCompleted())
- {
- // The Bonjour mDNSResponder somehow doesn't send goodbye message for the AAAA record when it is
- // removed by `DNSServiceRemoveRecord`. Per RFC 6762, a goodbye message of a record sets its TTL
- // to zero but the receiver should record the TTL of 1 and flushes the cache 1 second later. Here
- // we remove the AAAA record after updating its TTL to 1 second. This has the same effect as
- // sending a goodbye message.
- // TODO: resolve the goodbye issue with Bonjour mDNSResponder.
- dnsError = DNSServiceUpdateRecord(mServiceRef, recordRef, kDNSServiceFlagsUnique, sizeof(address.m8),
- address.m8, /* ttl */ 1);
- otbrLogResult(DNSErrorToOtbrError(dnsError), "Send goodbye message for host %s address %s: %s",
- MakeFullHostName(mName).c_str(), address.ToString().c_str(), DNSErrorToString(dnsError));
- }
- dnsError = DNSServiceRemoveRecord(mServiceRef, recordRef, /* flags */ 0);
- otbrLogResult(DNSErrorToOtbrError(dnsError), "Remove record for host %s address %s: %s",
- MakeFullHostName(mName).c_str(), address.ToString().c_str(), DNSErrorToString(dnsError));
- // TODO: ?
- // DNSRecordRefDeallocate(recordRef);
- }
+ fd = DNSServiceRefSockFD(mServiceRef);
+ VerifyOrExit(fd != -1);
+
+ FD_SET(fd, &aMainloop.mReadFdSet);
+ aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, fd);
exit:
return;
}
-Publisher::ServiceRegistration *PublisherMDnsSd::FindServiceRegistration(const DNSServiceRef &aServiceRef)
+void PublisherMDnsSd::DnssdServiceRegistration::Process(const MainloopContext &aMainloop,
+ std::vector<DNSServiceRef> &aReadyServices) const
{
- ServiceRegistration *result = nullptr;
+ int fd;
- for (auto &kv : mServiceRegistrations)
- {
- // We are sure that the service registrations must be instances of `DnssdServiceRegistration`.
- auto &serviceReg = static_cast<DnssdServiceRegistration &>(*kv.second);
+ VerifyOrExit(mServiceRef != nullptr);
- if (serviceReg.GetServiceRef() == aServiceRef)
- {
- result = kv.second.get();
- break;
- }
- }
+ fd = DNSServiceRefSockFD(mServiceRef);
+ VerifyOrExit(fd != -1);
- return result;
+ VerifyOrExit(FD_ISSET(fd, &aMainloop.mReadFdSet));
+ aReadyServices.push_back(mServiceRef);
+
+exit:
+ return;
}
-Publisher::HostRegistration *PublisherMDnsSd::FindHostRegistration(const DNSServiceRef &aServiceRef,
- const DNSRecordRef &aRecordRef)
+otbrError PublisherMDnsSd::DnssdServiceRegistration::Register(void)
{
- HostRegistration *result = nullptr;
+ std::string fullHostName;
+ std::string regType = MakeRegType(mType, mSubTypeList);
+ const char *hostNameCString = nullptr;
+ const char *serviceNameCString = nullptr;
+ DNSServiceErrorType dnsError;
+
+ if (!mHostName.empty())
+ {
+ fullHostName = MakeFullHostName(mHostName);
+ hostNameCString = fullHostName.c_str();
+ }
- for (auto &kv : mHostRegistrations)
+ if (!mName.empty())
{
- // We are sure that the host registrations must be instances of `DnssdHostRegistration`.
- auto &hostReg = static_cast<DnssdHostRegistration &>(*kv.second);
+ serviceNameCString = mName.c_str();
+ }
- if (hostReg.GetServiceRef() == aServiceRef && hostReg.GetRecordRefMap().count(aRecordRef))
- {
- result = kv.second.get();
- break;
- }
+ otbrLogInfo("Registering service %s.%s", mName.c_str(), regType.c_str());
+
+ dnsError = DNSServiceRegister(&mServiceRef, kDNSServiceFlagsNoAutoRename, kDNSServiceInterfaceIndexAny,
+ serviceNameCString, regType.c_str(),
+ /* domain */ nullptr, hostNameCString, htons(mPort), mTxtData.size(), mTxtData.data(),
+ HandleRegisterResult, this);
+
+ if (dnsError != kDNSServiceErr_NoError)
+ {
+ HandleRegisterResult(/* aFlags */ 0, dnsError);
}
- return result;
+ return GetPublisher().DnsErrorToOtbrError(dnsError);
}
-void PublisherMDnsSd::HandleServiceRegisterResult(DNSServiceRef aService,
- const DNSServiceFlags aFlags,
- DNSServiceErrorType aError,
- const char *aName,
- const char *aType,
- const char *aDomain,
- void *aContext)
+void PublisherMDnsSd::DnssdServiceRegistration::Unregister(void)
{
- static_cast<PublisherMDnsSd *>(aContext)->HandleServiceRegisterResult(aService, aFlags, aError, aName, aType,
- aDomain);
+ if (mServiceRef != nullptr)
+ {
+ GetPublisher().HandleServiceRefDeallocating(mServiceRef);
+ DNSServiceRefDeallocate(mServiceRef);
+ mServiceRef = nullptr;
+ }
}
-void PublisherMDnsSd::HandleServiceRegisterResult(DNSServiceRef aServiceRef,
- const DNSServiceFlags aFlags,
- DNSServiceErrorType aError,
- const char *aName,
- const char *aType,
- const char *aDomain)
+void PublisherMDnsSd::DnssdServiceRegistration::HandleRegisterResult(DNSServiceRef aServiceRef,
+ DNSServiceFlags aFlags,
+ DNSServiceErrorType aError,
+ const char *aName,
+ const char *aType,
+ const char *aDomain,
+ void *aContext)
{
+ OTBR_UNUSED_VARIABLE(aServiceRef);
+ OTBR_UNUSED_VARIABLE(aName);
+ OTBR_UNUSED_VARIABLE(aType);
OTBR_UNUSED_VARIABLE(aDomain);
- otbrError error = DNSErrorToOtbrError(aError);
- ServiceRegistration *serviceReg = FindServiceRegistration(aServiceRef);
- serviceReg->mName = aName;
+ static_cast<DnssdServiceRegistration *>(aContext)->HandleRegisterResult(aFlags, aError);
+}
- otbrLogInfo("Received reply for service %s.%s, serviceRef = %p", aName, aType, aServiceRef);
+void PublisherMDnsSd::DnssdServiceRegistration::HandleRegisterResult(DNSServiceFlags aFlags, DNSServiceErrorType aError)
+{
+ if ((aError == kDNSServiceErr_NoError) && (aFlags & kDNSServiceFlagsAdd))
+ {
+ otbrLogInfo("Successfully registered service %s.%s", mName.c_str(), mType.c_str());
+ Complete(OTBR_ERROR_NONE);
+ }
+ else
+ {
+ otbrLogErr("Failed to register service %s.%s: %s", mName.c_str(), mType.c_str(), DNSErrorToString(aError));
+ GetPublisher().RemoveServiceRegistration(mName, mType, DNSErrorToOtbrError(aError));
+ }
+}
+
+otbrError PublisherMDnsSd::DnssdHostRegistration::Register(void)
+{
+ DNSServiceErrorType dnsError = kDNSServiceErr_NoError;
- VerifyOrExit(serviceReg != nullptr);
+ otbrLogInfo("Registering new host %s", mName.c_str());
- if (aError == kDNSServiceErr_NoError && (aFlags & kDNSServiceFlagsAdd))
+ for (const Ip6Address &address : mAddresses)
{
- otbrLogInfo("Successfully registered service %s.%s", aName, aType);
- serviceReg->Complete(OTBR_ERROR_NONE);
+ DNSRecordRef recordRef = nullptr;
+
+ dnsError = GetPublisher().CreateSharedHostsRef();
+ VerifyOrExit(dnsError == kDNSServiceErr_NoError);
+
+ dnsError = DNSServiceRegisterRecord(GetPublisher().mHostsRef, &recordRef, kDNSServiceFlagsShared,
+ kDNSServiceInterfaceIndexAny, MakeFullHostName(mName).c_str(),
+ kDNSServiceType_AAAA, kDNSServiceClass_IN, sizeof(address.m8), address.m8,
+ /* ttl */ 0, HandleRegisterResult, this);
+ VerifyOrExit(dnsError == kDNSServiceErr_NoError);
+
+ mAddrRecordRefs.push_back(recordRef);
+ mAddrRegistered.push_back(false);
}
- else
+
+exit:
+ if ((dnsError != kDNSServiceErr_NoError) || mAddresses.empty())
{
- otbrLogErr("Failed to register service %s.%s: %s", aName, aType, DNSErrorToString(aError));
- RemoveServiceRegistration(serviceReg->mName, serviceReg->mType, error);
+ HandleRegisterResult(/* aRecordRef */ nullptr, dnsError);
+ }
+
+ return GetPublisher().DnsErrorToOtbrError(dnsError);
+}
+
+void PublisherMDnsSd::DnssdHostRegistration::Unregister(void)
+{
+ DNSServiceErrorType dnsError;
+
+ VerifyOrExit(GetPublisher().mHostsRef != nullptr);
+
+ for (size_t index = 0; index < mAddrRecordRefs.size(); index++)
+ {
+ const Ip6Address &address = mAddresses[index];
+
+ if (mAddrRegistered[index])
+ {
+ // The Bonjour mDNSResponder somehow doesn't send goodbye message for the AAAA record when it is
+ // removed by `DNSServiceRemoveRecord`. Per RFC 6762, a goodbye message of a record sets its TTL
+ // to zero but the receiver should record the TTL of 1 and flushes the cache 1 second later. Here
+ // we remove the AAAA record after updating its TTL to 1 second. This has the same effect as
+ // sending a goodbye message.
+ // TODO: resolve the goodbye issue with Bonjour mDNSResponder.
+ dnsError = DNSServiceUpdateRecord(GetPublisher().mHostsRef, mAddrRecordRefs[index], kDNSServiceFlagsUnique,
+ sizeof(address.m8), address.m8, /* ttl */ 1);
+ otbrLogResult(DNSErrorToOtbrError(dnsError), "Send goodbye message for host %s address %s: %s",
+ MakeFullHostName(mName).c_str(), address.ToString().c_str(), DNSErrorToString(dnsError));
+ }
+
+ dnsError = DNSServiceRemoveRecord(GetPublisher().mHostsRef, mAddrRecordRefs[index], /* flags */ 0);
+
+ otbrLogResult(DNSErrorToOtbrError(dnsError), "Remove record for host %s address %s: %s",
+ MakeFullHostName(mName).c_str(), address.ToString().c_str(), DNSErrorToString(dnsError));
}
exit:
- return;
+ mAddrRegistered.clear();
+ mAddrRecordRefs.clear();
+}
+
+void PublisherMDnsSd::DnssdHostRegistration::HandleRegisterResult(DNSServiceRef aServiceRef,
+ DNSRecordRef aRecordRef,
+ DNSServiceFlags aFlags,
+ DNSServiceErrorType aError,
+ void *aContext)
+{
+ OT_UNUSED_VARIABLE(aServiceRef);
+ OT_UNUSED_VARIABLE(aFlags);
+
+ static_cast<DnssdHostRegistration *>(aContext)->HandleRegisterResult(aRecordRef, aError);
+}
+
+void PublisherMDnsSd::DnssdHostRegistration::HandleRegisterResult(DNSRecordRef aRecordRef, DNSServiceErrorType aError)
+{
+ if (aError != kDNSServiceErr_NoError)
+ {
+ otbrLogErr("Failed to register host %s: %s", mName.c_str(), DNSErrorToString(aError));
+ GetPublisher().RemoveHostRegistration(mName, DNSErrorToOtbrError(aError));
+ }
+ else
+ {
+ bool shouldComplete = !IsCompleted();
+
+ for (size_t index = 0; index < mAddrRecordRefs.size(); index++)
+ {
+ if ((mAddrRecordRefs[index] == aRecordRef) && !mAddrRegistered[index])
+ {
+ mAddrRegistered[index] = true;
+ otbrLogInfo("Successfully registered host %s address %s", mName.c_str(),
+ mAddresses[index].ToString().c_str());
+ }
+
+ if (!mAddrRegistered[index])
+ {
+ shouldComplete = false;
+ }
+ }
+
+ if (shouldComplete)
+ {
+ otbrLogInfo("Successfully registered all host %s addresses", mName.c_str());
+ Complete(OTBR_ERROR_NONE);
+ }
+ }
}
otbrError PublisherMDnsSd::PublishServiceImpl(const std::string &aHostName,
@@ -490,62 +634,33 @@ otbrError PublisherMDnsSd::PublishServiceImpl(const std::string &aHostName,
const std::string &aType,
const SubTypeList &aSubTypeList,
uint16_t aPort,
- const TxtList &aTxtList,
+ const TxtData &aTxtData,
ResultCallback &&aCallback)
{
- otbrError ret = OTBR_ERROR_NONE;
- int error = 0;
- std::vector<uint8_t> txt;
- SubTypeList sortedSubTypeList = SortSubTypeList(aSubTypeList);
- TxtList sortedTxtList = SortTxtList(aTxtList);
- std::string regType = MakeRegType(aType, sortedSubTypeList);
- DNSServiceRef serviceRef = nullptr;
- std::string fullHostName;
- const char *hostNameCString = nullptr;
- const char *serviceNameCString = nullptr;
-
- VerifyOrExit(mState == State::kReady, ret = OTBR_ERROR_INVALID_STATE);
-
- if (!aHostName.empty())
- {
- fullHostName = MakeFullHostName(aHostName);
- hostNameCString = fullHostName.c_str();
- }
- if (!aName.empty())
+ otbrError error = OTBR_ERROR_NONE;
+ SubTypeList sortedSubTypeList = SortSubTypeList(aSubTypeList);
+ std::string regType = MakeRegType(aType, sortedSubTypeList);
+ DnssdServiceRegistration *serviceReg;
+
+ if (mState != State::kReady)
{
- serviceNameCString = aName.c_str();
+ error = OTBR_ERROR_INVALID_STATE;
+ std::move(aCallback)(error);
+ ExitNow();
}
- aCallback = HandleDuplicateServiceRegistration(aHostName, aName, aType, sortedSubTypeList, aPort, sortedTxtList,
+ aCallback = HandleDuplicateServiceRegistration(aHostName, aName, aType, sortedSubTypeList, aPort, aTxtData,
std::move(aCallback));
VerifyOrExit(!aCallback.IsNull());
- SuccessOrExit(ret = EncodeTxtData(aTxtList, txt));
- otbrLogInfo("Registering new service %s.%s.local, serviceRef = %p", aName.c_str(), regType.c_str(), serviceRef);
- SuccessOrExit(error = DNSServiceRegister(&serviceRef, kDNSServiceFlagsNoAutoRename, kDNSServiceInterfaceIndexAny,
- serviceNameCString, regType.c_str(),
- /* domain */ nullptr, hostNameCString, htons(aPort), txt.size(),
- txt.data(), HandleServiceRegisterResult, this));
- AddServiceRegistration(std::unique_ptr<DnssdServiceRegistration>(new DnssdServiceRegistration(
- aHostName, aName, aType, sortedSubTypeList, aPort, sortedTxtList, std::move(aCallback), serviceRef, this)));
+ serviceReg = new DnssdServiceRegistration(aHostName, aName, aType, sortedSubTypeList, aPort, aTxtData,
+ std::move(aCallback), this);
+ AddServiceRegistration(std::unique_ptr<DnssdServiceRegistration>(serviceReg));
-exit:
- if (error != kDNSServiceErr_NoError || ret != OTBR_ERROR_NONE)
- {
- if (error != kDNSServiceErr_NoError)
- {
- ret = DNSErrorToOtbrError(error);
- otbrLogErr("Failed to publish service %s.%s for mdnssd error: %s!", aName.c_str(), aType.c_str(),
- DNSErrorToString(error));
- }
+ error = serviceReg->Register();
- if (serviceRef != nullptr)
- {
- DNSServiceRefDeallocate(serviceRef);
- }
- std::move(aCallback)(ret);
- }
- return ret;
+exit:
+ return error;
}
void PublisherMDnsSd::UnpublishService(const std::string &aName, const std::string &aType, ResultCallback &&aCallback)
@@ -559,58 +674,30 @@ exit:
std::move(aCallback)(error);
}
-otbrError PublisherMDnsSd::PublishHostImpl(const std::string &aName,
- const std::vector<Ip6Address> &aAddresses,
- ResultCallback &&aCallback)
+otbrError PublisherMDnsSd::PublishHostImpl(const std::string &aName,
+ const AddressList &aAddresses,
+ ResultCallback &&aCallback)
{
- otbrError ret = OTBR_ERROR_NONE;
- int error = 0;
- std::string fullName;
- DnssdHostRegistration *registration;
-
- VerifyOrExit(mState == Publisher::State::kReady, ret = OTBR_ERROR_INVALID_STATE);
+ otbrError error = OTBR_ERROR_NONE;
+ DnssdHostRegistration *hostReg;
- fullName = MakeFullHostName(aName);
-
- aCallback = HandleDuplicateHostRegistration(aName, aAddresses, std::move(aCallback));
- VerifyOrExit(!aCallback.IsNull());
- VerifyOrExit(!aAddresses.empty(), std::move(aCallback)(OTBR_ERROR_NONE));
-
- if (mHostsRef == nullptr)
+ if (mState != State::kReady)
{
- SuccessOrExit(error = DNSServiceCreateConnection(&mHostsRef));
- otbrLogDebug("Created new DNSServiceRef for hosts: %p", mHostsRef);
+ error = OTBR_ERROR_INVALID_STATE;
+ std::move(aCallback)(error);
+ ExitNow();
}
- registration = new DnssdHostRegistration(aName, aAddresses, std::move(aCallback), mHostsRef, this);
+ aCallback = HandleDuplicateHostRegistration(aName, aAddresses, std::move(aCallback));
+ VerifyOrExit(!aCallback.IsNull());
- otbrLogInfo("Registering new host %s", aName.c_str());
- for (const auto &address : aAddresses)
- {
- DNSRecordRef recordRef = nullptr;
- // Supports only IPv6 for now, may support IPv4 in the future.
- SuccessOrExit(error = DNSServiceRegisterRecord(mHostsRef, &recordRef, kDNSServiceFlagsShared,
- kDNSServiceInterfaceIndexAny, fullName.c_str(),
- kDNSServiceType_AAAA, kDNSServiceClass_IN, sizeof(address.m8),
- address.m8, /* ttl */ 0, HandleRegisterHostResult, this));
- registration->GetRecordRefMap()[recordRef] = address;
- }
+ hostReg = new DnssdHostRegistration(aName, aAddresses, std::move(aCallback), this);
+ AddHostRegistration(std::unique_ptr<DnssdHostRegistration>(hostReg));
- AddHostRegistration(std::unique_ptr<DnssdHostRegistration>(registration));
+ error = hostReg->Register();
exit:
- if (error != kDNSServiceErr_NoError || ret != OTBR_ERROR_NONE)
- {
- if (error != kDNSServiceErr_NoError)
- {
- ret = DNSErrorToOtbrError(error);
- otbrLogErr("Failed to publish/update host %s for mdnssd error: %s!", aName.c_str(),
- DNSErrorToString(error));
- }
-
- std::move(aCallback)(ret);
- }
- return ret;
+ return error;
}
void PublisherMDnsSd::UnpublishHost(const std::string &aName, ResultCallback &&aCallback)
@@ -627,52 +714,6 @@ exit:
std::move(aCallback)(error);
}
-void PublisherMDnsSd::HandleRegisterHostResult(DNSServiceRef aServiceRef,
- DNSRecordRef aRecordRef,
- DNSServiceFlags aFlags,
- DNSServiceErrorType aError,
- void *aContext)
-{
- static_cast<PublisherMDnsSd *>(aContext)->HandleRegisterHostResult(aServiceRef, aRecordRef, aFlags, aError);
-}
-
-void PublisherMDnsSd::HandleRegisterHostResult(DNSServiceRef aServiceRef,
- DNSRecordRef aRecordRef,
- DNSServiceFlags aFlags,
- DNSServiceErrorType aError)
-{
- OTBR_UNUSED_VARIABLE(aFlags);
-
- otbrError error = DNSErrorToOtbrError(aError);
- auto *hostReg = static_cast<DnssdHostRegistration *>(FindHostRegistration(aServiceRef, aRecordRef));
-
- std::string hostName;
-
- VerifyOrExit(hostReg != nullptr);
-
- hostName = MakeFullHostName(hostReg->mName);
-
- otbrLogInfo("Received reply for host %s: %s", hostName.c_str(), DNSErrorToString(aError));
-
- if (error == OTBR_ERROR_NONE)
- {
- --hostReg->mCallbackCount;
- if (!hostReg->mCallbackCount)
- {
- otbrLogInfo("Successfully registered host %s", hostName.c_str());
- hostReg->Complete(OTBR_ERROR_NONE);
- }
- }
- else
- {
- otbrLogWarning("Failed to register host %s for mdnssd error: %s", hostName.c_str(), DNSErrorToString(aError));
- RemoveHostRegistration(hostReg->mName, error);
- }
-
-exit:
- return;
-}
-
// See `regtype` parameter of the DNSServiceRegister() function for more information.
std::string PublisherMDnsSd::MakeRegType(const std::string &aType, SubTypeList aSubTypeList)
{
@@ -797,6 +838,7 @@ void PublisherMDnsSd::ServiceRef::DeallocateServiceRef(void)
{
if (mServiceRef != nullptr)
{
+ mPublisher.HandleServiceRefDeallocating(mServiceRef);
DNSServiceRefDeallocate(mServiceRef);
mServiceRef = nullptr;
}
@@ -878,13 +920,13 @@ void PublisherMDnsSd::ServiceSubscription::HandleBrowseResult(DNSServiceRef
}
else
{
- mMDnsSd->OnServiceRemoved(aInterfaceIndex, mType, aInstanceName);
+ mPublisher.OnServiceRemoved(aInterfaceIndex, mType, aInstanceName);
}
exit:
if (aErrorCode != kDNSServiceErr_NoError)
{
- mMDnsSd->OnServiceResolveFailed(mType, mInstanceName, aErrorCode);
+ mPublisher.OnServiceResolveFailed(mType, mInstanceName, aErrorCode);
Release();
}
}
@@ -937,12 +979,11 @@ void PublisherMDnsSd::ServiceInstanceResolution::Resolve(void)
{
assert(mServiceRef == nullptr);
- mSubscription->mMDnsSd->mServiceInstanceResolutionBeginTime[std::make_pair(mInstanceName, mTypeEndWithDot)] =
- Clock::now();
+ mSubscription->mPublisher.mServiceInstanceResolutionBeginTime[std::make_pair(mInstanceName, mType)] = Clock::now();
- otbrLogInfo("DNSServiceResolve %s %s inf %u", mInstanceName.c_str(), mTypeEndWithDot.c_str(), mNetifIndex);
+ otbrLogInfo("DNSServiceResolve %s %s inf %u", mInstanceName.c_str(), mType.c_str(), mNetifIndex);
DNSServiceResolve(&mServiceRef, /* flags */ kDNSServiceFlagsTimeout, mNetifIndex, mInstanceName.c_str(),
- mTypeEndWithDot.c_str(), mDomain.c_str(), HandleResolveResult, this);
+ mType.c_str(), mDomain.c_str(), HandleResolveResult, this);
}
void PublisherMDnsSd::ServiceInstanceResolution::HandleResolveResult(DNSServiceRef aServiceRef,
@@ -1002,7 +1043,7 @@ exit:
if (aErrorCode != kDNSServiceErr_NoError || error != OTBR_ERROR_NONE)
{
- mSubscription->mMDnsSd->OnServiceResolveFailed(mSubscription->mType, mInstanceName, aErrorCode);
+ mSubscription->mPublisher.OnServiceResolveFailed(mSubscription->mType, mInstanceName, aErrorCode);
FinishResolution();
}
}
@@ -1087,7 +1128,7 @@ void PublisherMDnsSd::ServiceInstanceResolution::FinishResolution(void)
subscription->RemoveInstanceResolution(*this);
// NOTE: The `ServiceSubscription` object may be freed in `OnServiceResolved`.
- subscription->mMDnsSd->OnServiceResolved(serviceName, instanceInfo);
+ subscription->mPublisher.OnServiceResolved(serviceName, instanceInfo);
}
void PublisherMDnsSd::HostSubscription::Resolve(void)
@@ -1096,7 +1137,7 @@ void PublisherMDnsSd::HostSubscription::Resolve(void)
assert(mServiceRef == nullptr);
- mMDnsSd->mHostResolutionBeginTime[mHostName] = Clock::now();
+ mPublisher.mHostResolutionBeginTime[mHostName] = Clock::now();
otbrLogInfo("DNSServiceGetAddrInfo %s inf %d", fullHostName.c_str(), kDNSServiceInterfaceIndexAny);
@@ -1149,12 +1190,12 @@ void PublisherMDnsSd::HostSubscription::HandleResolveResult(DNSServiceRef
otbrLogInfo("DNSServiceGetAddrInfo reply: address=%s, ttl=%" PRIu32, address.ToString().c_str(), aTtl);
// NOTE: This `HostSubscription` object may be freed in `OnHostResolved`.
- mMDnsSd->OnHostResolved(mHostName, mHostInfo);
+ mPublisher.OnHostResolved(mHostName, mHostInfo);
exit:
if (aErrorCode != kDNSServiceErr_NoError)
{
- mMDnsSd->OnHostResolveFailed(aHostName, aErrorCode);
+ mPublisher.OnHostResolveFailed(aHostName, aErrorCode);
}
}
diff --git a/src/mdns/mdns_mdnssd.hpp b/src/mdns/mdns_mdnssd.hpp
index 8321811b..fa4bf4d1 100644
--- a/src/mdns/mdns_mdnssd.hpp
+++ b/src/mdns/mdns_mdnssd.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_AGENT_MDNS_MDNSSD_HPP_
#define OTBR_AGENT_MDNS_MDNSSD_HPP_
+#include "openthread-br/config.h"
+
#include <array>
#include <map>
#include <memory>
@@ -74,7 +76,7 @@ public:
void UnsubscribeHost(const std::string &aHostName) override;
otbrError Start(void) override;
bool IsStarted(void) const override;
- void Stop(void) override;
+ void Stop(void) override { Stop(kNormalStop); }
// Implementation of MainloopProcessor.
@@ -87,11 +89,11 @@ protected:
const std::string &aType,
const SubTypeList &aSubTypeList,
uint16_t aPort,
- const TxtList &aTxtList,
+ const TxtData &aTxtData,
ResultCallback &&aCallback) override;
- otbrError PublishHostImpl(const std::string &aName,
- const std::vector<Ip6Address> &aAddress,
- ResultCallback &&aCallback) override;
+ otbrError PublishHostImpl(const std::string &aName,
+ const AddressList &aAddress,
+ ResultCallback &&aCallback) override;
void OnServiceResolveFailedImpl(const std::string &aType,
const std::string &aInstanceName,
int32_t aErrorCode) override;
@@ -101,71 +103,69 @@ protected:
private:
static constexpr uint32_t kDefaultTtl = 10;
+ enum StopMode : uint8_t
+ {
+ kNormalStop,
+ kStopOnServiceNotRunningError,
+ };
+
class DnssdServiceRegistration : public ServiceRegistration
{
public:
- DnssdServiceRegistration(const std::string &aHostName,
- const std::string &aName,
- const std::string &aType,
- const SubTypeList &aSubTypeList,
- uint16_t aPort,
- const TxtList &aTxtList,
- ResultCallback &&aCallback,
- DNSServiceRef aServiceRef,
- PublisherMDnsSd *aPublisher)
- : ServiceRegistration(aHostName,
- aName,
- aType,
- aSubTypeList,
- aPort,
- aTxtList,
- std::move(aCallback),
- aPublisher)
- , mServiceRef(aServiceRef)
- {
- }
+ using ServiceRegistration::ServiceRegistration; // Inherit base constructor
- ~DnssdServiceRegistration(void) override;
- const DNSServiceRef &GetServiceRef() const { return mServiceRef; }
+ ~DnssdServiceRegistration(void) override { Unregister(); }
+
+ void Update(MainloopContext &aMainloop) const;
+ void Process(const MainloopContext &aMainloop, std::vector<DNSServiceRef> &aReadyServices) const;
+ otbrError Register(void);
private:
- DNSServiceRef mServiceRef;
+ void Unregister(void);
+ PublisherMDnsSd &GetPublisher(void) { return *static_cast<PublisherMDnsSd *>(mPublisher); }
+ void HandleRegisterResult(DNSServiceFlags aFlags, DNSServiceErrorType aError);
+ static void HandleRegisterResult(DNSServiceRef aServiceRef,
+ DNSServiceFlags aFlags,
+ DNSServiceErrorType aError,
+ const char *aName,
+ const char *aType,
+ const char *aDomain,
+ void *aContext);
+
+ DNSServiceRef mServiceRef = nullptr;
};
class DnssdHostRegistration : public HostRegistration
{
public:
- DnssdHostRegistration(const std::string &aName,
- const std::vector<Ip6Address> &aAddresses,
- ResultCallback &&aCallback,
- DNSServiceRef aServiceRef,
- Publisher *aPublisher)
- : HostRegistration(aName, aAddresses, std::move(aCallback), aPublisher)
- , mServiceRef(aServiceRef)
- , mRecordRefMap()
- , mCallbackCount(aAddresses.size())
- {
- }
+ using HostRegistration::HostRegistration; // Inherit base class constructor
- ~DnssdHostRegistration(void) override;
- const DNSServiceRef &GetServiceRef() const { return mServiceRef; }
- const std::map<DNSRecordRef, Ip6Address> &GetRecordRefMap() const { return mRecordRefMap; }
- std::map<DNSRecordRef, Ip6Address> &GetRecordRefMap() { return mRecordRefMap; }
+ ~DnssdHostRegistration(void) override { Unregister(); }
- private:
- DNSServiceRef mServiceRef;
+ otbrError Register(void);
- public:
- std::map<DNSRecordRef, Ip6Address> mRecordRefMap;
- uint32_t mCallbackCount;
+ private:
+ void Unregister(void);
+ PublisherMDnsSd &GetPublisher(void) { return *static_cast<PublisherMDnsSd *>(mPublisher); }
+ void HandleRegisterResult(DNSRecordRef aRecordRef, DNSServiceErrorType aError);
+ static void HandleRegisterResult(DNSServiceRef aServiceRef,
+ DNSRecordRef aRecordRef,
+ DNSServiceFlags aFlags,
+ DNSServiceErrorType aErrorCode,
+ void *aContext);
+
+ std::vector<DNSRecordRef> mAddrRecordRefs;
+ std::vector<bool> mAddrRegistered;
};
struct ServiceRef : private ::NonCopyable
{
- DNSServiceRef mServiceRef;
+ DNSServiceRef mServiceRef;
+ PublisherMDnsSd &mPublisher;
- explicit ServiceRef(void)
+ explicit ServiceRef(PublisherMDnsSd &aPublisher)
: mServiceRef(nullptr)
+ , mPublisher(aPublisher)
{
}
@@ -186,10 +186,10 @@ private:
std::string aType,
std::string aDomain,
uint32_t aNetifIndex)
- : ServiceRef()
+ : ServiceRef(aSubscription.mPublisher)
, mSubscription(&aSubscription)
, mInstanceName(std::move(aInstanceName))
- , mTypeEndWithDot(std::move(aType))
+ , mType(std::move(aType))
, mDomain(std::move(aDomain))
, mNetifIndex(aNetifIndex)
{
@@ -236,7 +236,7 @@ private:
ServiceSubscription *mSubscription;
std::string mInstanceName;
- std::string mTypeEndWithDot;
+ std::string mType;
std::string mDomain;
uint32_t mNetifIndex;
DiscoveredInstanceInfo mInstanceInfo;
@@ -244,9 +244,8 @@ private:
struct ServiceSubscription : public ServiceRef
{
- explicit ServiceSubscription(PublisherMDnsSd &aMDnsSd, std::string aType, std::string aInstanceName)
- : ServiceRef()
- , mMDnsSd(&aMDnsSd)
+ explicit ServiceSubscription(PublisherMDnsSd &aPublisher, std::string aType, std::string aInstanceName)
+ : ServiceRef(aPublisher)
, mType(std::move(aType))
, mInstanceName(std::move(aInstanceName))
{
@@ -277,18 +276,16 @@ private:
const char *aType,
const char *aDomain);
- PublisherMDnsSd *mMDnsSd;
- std::string mType;
- std::string mInstanceName;
+ std::string mType;
+ std::string mInstanceName;
std::vector<std::unique_ptr<ServiceInstanceResolution>> mResolvingInstances;
};
struct HostSubscription : public ServiceRef
{
- explicit HostSubscription(PublisherMDnsSd &aMDnsSd, std::string aHostName)
- : ServiceRef()
- , mMDnsSd(&aMDnsSd)
+ explicit HostSubscription(PublisherMDnsSd &aPublisher, std::string aHostName)
+ : ServiceRef(aPublisher)
, mHostName(std::move(aHostName))
{
}
@@ -310,7 +307,6 @@ private:
const struct sockaddr *aAddress,
uint32_t aTtl);
- PublisherMDnsSd *mMDnsSd;
std::string mHostName;
DiscoveredHostInfo mHostInfo;
};
@@ -318,33 +314,12 @@ private:
using ServiceSubscriptionList = std::vector<std::unique_ptr<ServiceSubscription>>;
using HostSubscriptionList = std::vector<std::unique_ptr<HostSubscription>>;
- static void HandleServiceRegisterResult(DNSServiceRef aService,
- const DNSServiceFlags aFlags,
- DNSServiceErrorType aError,
- const char *aName,
- const char *aType,
- const char *aDomain,
- void *aContext);
- void HandleServiceRegisterResult(DNSServiceRef aService,
- const DNSServiceFlags aFlags,
- DNSServiceErrorType aError,
- const char *aName,
- const char *aType,
- const char *aDomain);
- static void HandleRegisterHostResult(DNSServiceRef aHostsConnection,
- DNSRecordRef aHostRecord,
- DNSServiceFlags aFlags,
- DNSServiceErrorType aErrorCode,
- void *aContext);
- void HandleRegisterHostResult(DNSServiceRef aHostsConnection,
- DNSRecordRef aHostRecord,
- DNSServiceFlags aFlags,
- DNSServiceErrorType aErrorCode);
-
static std::string MakeRegType(const std::string &aType, SubTypeList aSubTypeList);
- ServiceRegistration *FindServiceRegistration(const DNSServiceRef &aServiceRef);
- HostRegistration *FindHostRegistration(const DNSServiceRef &aServiceRef, const DNSRecordRef &aRecordRef);
+ void Stop(StopMode aStopMode);
+ DNSServiceErrorType CreateSharedHostsRef(void);
+ void DeallocateHostsRef(void);
+ void HandleServiceRefDeallocating(const DNSServiceRef &aServiceRef);
DNSServiceRef mHostsRef;
State mState;
@@ -352,6 +327,8 @@ private:
ServiceSubscriptionList mSubscribedServices;
HostSubscriptionList mSubscribedHosts;
+
+ std::vector<DNSServiceRef> mServiceRefsToProcess;
};
/**
diff --git a/src/ncp/CMakeLists.txt b/src/ncp/CMakeLists.txt
index c2eac7a6..1540225b 100644
--- a/src/ncp/CMakeLists.txt
+++ b/src/ncp/CMakeLists.txt
@@ -34,10 +34,5 @@ add_library(otbr-ncp
target_link_libraries(otbr-ncp PRIVATE
otbr-common
$<$<BOOL:${OTBR_FEATURE_FLAGS}>:otbr-proto>
+ $<$<BOOL:${OTBR_TELEMETRY_DATA_API}>:otbr-proto>
)
-
-if(OTBR_FEATURE_FLAGS)
- target_include_directories(otbr-ncp PRIVATE
- ${PROJECT_SOURCE_DIR}/build/src
- )
-endif()
diff --git a/src/ncp/ncp_openthread.cpp b/src/ncp/ncp_openthread.cpp
index 0f4302c2..efd3e7f0 100644
--- a/src/ncp/ncp_openthread.cpp
+++ b/src/ncp/ncp_openthread.cpp
@@ -35,6 +35,7 @@
#include <string.h>
#include <openthread/backbone_router_ftd.h>
+#include <openthread/border_routing.h>
#include <openthread/dataset.h>
#include <openthread/dnssd_server.h>
#include <openthread/logging.h>
@@ -59,9 +60,10 @@
namespace otbr {
namespace Ncp {
-static const uint16_t kThreadVersion11 = 2; ///< Thread Version 1.1
-static const uint16_t kThreadVersion12 = 3; ///< Thread Version 1.2
-static const uint16_t kThreadVersion13 = 4; ///< Thread Version 1.3
+static const uint16_t kThreadVersion11 = 2; ///< Thread Version 1.1
+static const uint16_t kThreadVersion12 = 3; ///< Thread Version 1.2
+static const uint16_t kThreadVersion13 = 4; ///< Thread Version 1.3
+static const uint16_t kThreadVersion131 = 5; ///< Thread Version 1.3.1
ControllerOpenThread::ControllerOpenThread(const char *aInterfaceName,
const std::vector<const char *> &aRadioUrls,
@@ -243,6 +245,9 @@ void ControllerOpenThread::Init(void)
#if OTBR_ENABLE_DNS_UPSTREAM_QUERY
otDnssdUpstreamQuerySetEnabled(mInstance, /* aEnabled */ true);
#endif
+#if OTBR_ENABLE_DHCP6_PD
+ otBorderRoutingDhcp6PdSetEnabled(mInstance, /* aEnabled */ true);
+#endif
#endif // OTBR_ENABLE_FEATURE_FLAGS
mThreadHelper = std::unique_ptr<otbr::agent::ThreadHelper>(new otbr::agent::ThreadHelper(mInstance, this));
@@ -277,6 +282,9 @@ otError ControllerOpenThread::ApplyFeatureFlagList(const FeatureFlagList &aFeatu
#if OTBR_ENABLE_DNS_UPSTREAM_QUERY
otDnssdUpstreamQuerySetEnabled(mInstance, aFeatureFlagList.enable_dns_upstream_query());
#endif
+#if OTBR_ENABLE_DHCP6_PD
+ otBorderRoutingDhcp6PdSetEnabled(mInstance, aFeatureFlagList.enable_dhcp6_pd());
+#endif
return error;
}
@@ -377,6 +385,9 @@ const char *ControllerOpenThread::GetThreadVersion(void)
case kThreadVersion13:
version = "1.3.0";
break;
+ case kThreadVersion131:
+ version = "1.3.1";
+ break;
default:
otbrLogEmerg("Unexpected thread version %hu", otThreadGetVersion());
exit(-1);
diff --git a/src/ncp/ncp_openthread.hpp b/src/ncp/ncp_openthread.hpp
index 58779ddc..51ba31f4 100644
--- a/src/ncp/ncp_openthread.hpp
+++ b/src/ncp/ncp_openthread.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_AGENT_NCP_OPENTHREAD_HPP_
#define OTBR_AGENT_NCP_OPENTHREAD_HPP_
+#include "openthread-br/config.h"
+
#include <chrono>
#include <memory>
diff --git a/src/openwrt/otbr-agent.init.in b/src/openwrt/otbr-agent.init.in
index 2c1de2f2..46505ff4 100755
--- a/src/openwrt/otbr-agent.init.in
+++ b/src/openwrt/otbr-agent.init.in
@@ -34,8 +34,11 @@ USE_PROCD=1
start_service()
{
local uci_thread_if_name=$(uci -q get otbr-agent.service.thread_if_name)
+ local uci_infra_if_name=$(uci -q get otbr-agent.service.infra_if_name)
+ local uci_uart_device=$(uci -q get otbr-agent.service.uart_device)
+ local uci_uart_baudrate=$(uci -q get otbr-agent.service.uart_baudrate)
procd_open_instance
- procd_set_param command @CMAKE_INSTALL_FULL_SBINDIR@/otbr-agent -I $uci_thread_if_name spinel+hdlc+uart:///dev/ttyACM0
+ procd_set_param command @CMAKE_INSTALL_FULL_SBINDIR@/otbr-agent -I $uci_thread_if_name -B $uci_infra_if_name spinel+hdlc+uart://$uci_uart_device?uart-baudrate=$uci_uart_baudrate trel://$uci_infra_if_name
procd_close_instance
}
diff --git a/src/openwrt/otbr-agent.uci-config.in b/src/openwrt/otbr-agent.uci-config.in
index 7d8473c5..855d7088 100644
--- a/src/openwrt/otbr-agent.uci-config.in
+++ b/src/openwrt/otbr-agent.uci-config.in
@@ -1,2 +1,5 @@
config otbr-agent 'service'
option thread_if_name "wpan0"
+ option infra_if_name "eth0"
+ option uart_device "/dev/ttyACM0"
+ option uart_baudrate 115200
diff --git a/src/openwrt/view/admin_thread/thread_view.htm b/src/openwrt/view/admin_thread/thread_view.htm
index bc30bfd8..47372104 100644
--- a/src/openwrt/view/admin_thread/thread_view.htm
+++ b/src/openwrt/view/admin_thread/thread_view.htm
@@ -116,7 +116,7 @@
</div>
<%+footer%>
-<script src='http://d3js.org/d3.v4.min.js'></script>
+<script src='//d3js.org/d3.v4.min.js'></script>
<script type="text/javascript" src="/luci-static/resources/handle_error.js"></script>
<script type="text/javascript">//<![CDATA[
handle_error(GetURLParameter('error'));
diff --git a/src/proto/CMakeLists.txt b/src/proto/CMakeLists.txt
index 28e83dce..e982d52a 100644
--- a/src/proto/CMakeLists.txt
+++ b/src/proto/CMakeLists.txt
@@ -1,3 +1,11 @@
+# Config brew protobuf version for Mac, see .github/workflows/macOS.yml
+if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+ set(Protobuf_PREFIX_PATH
+ "/usr/local/opt/protobuf@21/include"
+ "/usr/local/opt/protobuf@21/lib"
+ "/usr/local/opt/protobuf@21/bin")
+ list(APPEND CMAKE_PREFIX_PATH "${Protobuf_PREFIX_PATH}")
+endif()
find_package(Protobuf REQUIRED)
# Set up the output path.
@@ -50,6 +58,11 @@ add_library(otbr-proto STATIC
)
find_package(Protobuf REQUIRED)
-target_link_libraries(otbr-proto
+
+target_link_libraries(otbr-proto PUBLIC
protobuf::libprotobuf-lite
)
+
+target_include_directories(otbr-proto PUBLIC
+ ${PROJECT_SOURCE_DIR}/build/src
+)
diff --git a/src/proto/capabilities.proto b/src/proto/capabilities.proto
new file mode 100644
index 00000000..f49864ac
--- /dev/null
+++ b/src/proto/capabilities.proto
@@ -0,0 +1,14 @@
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package otbr;
+
+// Capabilities message exposes a list of values of the macros of ot-br-posix. This is not an exhaustive
+// list of all macros of ot-br-posix.
+message Capabilities {
+ // Each of the following items matches exactly one macro, i.e. OTBR_ENABLE_NAT64 -> nat64
+ // When some macro is deleted, the corresponding value should be marked as "reserved".
+ // It is suggested to assign a new field number to a macro when its scope has significantly changed.
+ optional bool nat64 = 1; // OTBR_ENABLE_NAT64
+ optional bool dhcp6_pd = 2; // OTBR_ENABLE_DHCP6_PD
+}
diff --git a/src/proto/feature_flag.proto b/src/proto/feature_flag.proto
index 1b1cf01b..2b82b896 100644
--- a/src/proto/feature_flag.proto
+++ b/src/proto/feature_flag.proto
@@ -62,4 +62,6 @@ message FeatureFlagList {
optional bool enable_trel = 4 [default = false];
// Whether to enable upstream DNS forwarding.
optional bool enable_dns_upstream_query = 5 [default = false];
+ // Whether to enable prefix delegation.
+ optional bool enable_dhcp6_pd = 6 [default = false];
}
diff --git a/src/proto/thread_telemetry.proto b/src/proto/thread_telemetry.proto
new file mode 100644
index 00000000..8ffcadf9
--- /dev/null
+++ b/src/proto/thread_telemetry.proto
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 2023, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package threadnetwork;
+
+// Thread Telemetry data definition.
+// The field range for your data definition is determined as:
+// ---------------------------------------------------------------------------
+// | Field Range | Logging From
+// | [1 - 500) | Primary fields logged from OTBR-agent/OpenThread.
+// | [500-600) | OTBR vendor fields logged from OTBR-agent/OpenThread.
+// | Other | Reserved for now.
+// ---------------------------------------------------------------------------
+// Usage:
+// Delete field: do not directly delete field. Deprecate it instead.
+message TelemetryData {
+ message Duration {
+ // Signed seconds of the span of time. Must be from -315,576,000,000
+ // to +315,576,000,000 inclusive. Note: these bounds are computed from:
+ // 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
+ optional int64 seconds = 1;
+
+ // Signed fractions of a second at nanosecond resolution of the span
+ // of time. Durations less than one second are represented with a 0
+ // `seconds` field and a positive or negative `nanos` field. For durations
+ // of one second or more, a non-zero value for the `nanos` field must be
+ // of the same sign as the `seconds` field. Must be from -999,999,999
+ // to +999,999,999 inclusive.
+ optional int32 nanos = 2;
+ }
+
+ message WpanStats {
+ optional int32 phy_rx = 1;
+ optional int32 phy_tx = 2;
+ optional int32 mac_unicast_rx = 3;
+ optional int32 mac_unicast_tx = 4;
+ optional int32 mac_broadcast_rx = 5;
+ optional int32 mac_broadcast_tx = 6;
+ optional int32 mac_tx_ack_req = 7;
+ optional int32 mac_tx_no_ack_req = 8;
+ optional int32 mac_tx_acked = 9;
+ optional int32 mac_tx_data = 10;
+ optional int32 mac_tx_data_poll = 11;
+ optional int32 mac_tx_beacon = 12;
+ optional int32 mac_tx_beacon_req = 13;
+ optional int32 mac_tx_other_pkt = 14;
+ optional int32 mac_tx_retry = 15;
+ optional int32 mac_rx_data = 16;
+ optional int32 mac_rx_data_poll = 17;
+ optional int32 mac_rx_beacon = 18;
+ optional int32 mac_rx_beacon_req = 19;
+ optional int32 mac_rx_other_pkt = 20;
+ optional int32 mac_rx_filter_whitelist = 21;
+ optional int32 mac_rx_filter_dest_addr = 22;
+ optional int32 mac_tx_fail_cca = 23;
+ optional int32 mac_rx_fail_decrypt = 24;
+ optional int32 mac_rx_fail_no_frame = 25;
+ optional int32 mac_rx_fail_unknown_neighbor = 26;
+ optional int32 mac_rx_fail_invalid_src_addr = 27;
+ optional int32 mac_rx_fail_fcs = 28;
+ optional int32 mac_rx_fail_other = 29;
+ optional int32 ip_tx_success = 30;
+ optional int32 ip_rx_success = 31;
+ optional int32 ip_tx_failure = 32;
+ optional int32 ip_rx_failure = 33;
+ optional uint32 node_type = 34;
+ optional uint32 channel = 35;
+ optional int32 radio_tx_power = 36;
+ optional float mac_cca_fail_rate = 37;
+ }
+
+ message WpanTopoFull {
+ optional uint32 rloc16 = 1;
+ optional uint32 router_id = 2;
+ optional uint32 leader_router_id = 3;
+ // Deprecate bytes ext_address.
+ reserved 4;
+ optional bytes leader_address = 5;
+ optional uint32 leader_weight = 6;
+ optional uint32 leader_local_weight = 7;
+ optional bytes network_data = 8;
+ optional uint32 network_data_version = 9;
+ optional bytes stable_network_data = 10;
+ optional uint32 stable_network_data_version = 11;
+ optional uint32 preferred_router_id = 12;
+ optional uint32 partition_id = 13;
+ optional uint32 child_table_size = 14;
+ optional uint32 neighbor_table_size = 15;
+ optional int32 instant_rssi = 16;
+ optional uint64 extended_pan_id = 17;
+ }
+
+ message TopoEntry {
+ // deprecate bytes ext_address.
+ reserved 1;
+ optional uint32 rloc16 = 2;
+ // link quality with data range: 0~3.
+ optional uint32 link_quality_in = 3;
+ // the most recent RSSI measurement (8 bit).
+ optional int32 average_rssi = 4;
+ optional Duration age = 5;
+ optional bool rx_on_when_idle = 6;
+ optional bool full_function = 7;
+ optional bool secure_data_request = 8;
+ optional bool full_network_data = 9;
+ optional int32 last_rssi = 10;
+ optional uint32 link_frame_counter = 11;
+ optional uint32 mle_frame_counter = 12;
+ optional bool is_child = 13;
+ optional Duration timeout = 14;
+ optional uint32 network_data_version = 15;
+ optional float mac_frame_error_rate = 16;
+ optional float ip_message_error_rate = 17;
+ optional int32 version = 18;
+ }
+
+ enum NodeType {
+ NODE_TYPE_UNSPECIFIED = 0;
+ NODE_TYPE_ROUTER = 1;
+ NODE_TYPE_END = 2;
+ NODE_TYPE_SLEEPY_END = 3;
+ NODE_TYPE_MINIMAL_END = 4;
+
+ NODE_TYPE_OFFLINE = 5;
+ NODE_TYPE_DISABLED = 6;
+ NODE_TYPE_DETACHED = 7;
+
+ NODE_TYPE_NL_LURKER = 0x10;
+ NODE_TYPE_COMMISSIONER = 0x20;
+ NODE_TYPE_LEADER = 0x40;
+ }
+
+ message PacketsAndBytes {
+ optional int64 packet_count = 1;
+ optional int64 byte_count = 2;
+ }
+
+ message Nat64TrafficCounters {
+ optional int64 ipv4_to_ipv6_packets = 1;
+ optional int64 ipv4_to_ipv6_bytes = 2;
+ optional int64 ipv6_to_ipv4_packets = 3;
+ optional int64 ipv6_to_ipv4_bytes = 4;
+ }
+
+ message Nat64ProtocolCounters {
+ optional Nat64TrafficCounters tcp = 1;
+ optional Nat64TrafficCounters udp = 2;
+ optional Nat64TrafficCounters icmp = 3;
+ }
+
+ message Nat64PacketCounters {
+ optional int64 ipv4_to_ipv6_packets = 1;
+ optional int64 ipv6_to_ipv4_packets = 2;
+ }
+
+ message Nat64ErrorCounters {
+ optional Nat64PacketCounters unknown = 1;
+ optional Nat64PacketCounters illegal_packet = 2;
+ optional Nat64PacketCounters unsupported_protocol = 3;
+ optional Nat64PacketCounters no_mapping = 4;
+ }
+
+ message BorderRoutingCounters {
+ reserved 1 to 8;
+ // The number of Router Advertisement packets received by otbr-agent on the
+ // infra link
+ optional int64 ra_rx = 9;
+
+ // The number of Router Advertisement packets successfully transmitted by
+ // otbr-agent on the infra link.
+ optional int64 ra_tx_success = 10;
+
+ // The number of Router Advertisement packets failed to transmit by
+ // otbr-agent on the infra link.
+ optional int64 ra_tx_failure = 11;
+
+ // The number of Router Solicitation packets received by otbr-agent on the
+ // infra link
+ optional int64 rs_rx = 12;
+
+ // The number of Router Solicitation packets successfully transmitted by
+ // otbr-agent on the infra link.
+ optional int64 rs_tx_success = 13;
+
+ // The number of Router Solicitation packets failed to transmit by
+ // otbr-agent on the infra link.
+ optional int64 rs_tx_failure = 14;
+
+ // The counters for inbound unicast packets
+ optional PacketsAndBytes inbound_unicast = 15;
+
+ // The counters for inbound multicast packets
+ optional PacketsAndBytes inbound_multicast = 16;
+
+ // The counters for outbound unicast packets
+ optional PacketsAndBytes outbound_unicast = 17;
+
+ // The counters for outbound multicast packets
+ optional PacketsAndBytes outbound_multicast = 18;
+
+ // The inbound and outbound NAT64 traffic through the border router
+ optional Nat64ProtocolCounters nat64_protocol_counters = 19;
+
+ // Error counters for NAT64 translator on the border router
+ optional Nat64ErrorCounters nat64_error_counters = 20;
+ }
+
+ message SrpServerRegistrationInfo {
+ // The number of active hosts/services registered on the SRP server.
+ optional uint32 fresh_count = 1;
+
+ // The number of hosts/services in 'Deleted' state on the SRP server.
+ optional uint32 deleted_count = 2;
+
+ // The sum of lease time in milliseconds of all active hosts/services on the
+ // SRP server.
+ optional uint64 lease_time_total_ms = 3;
+
+ // The sum of key lease time in milliseconds of all active hosts/services on
+ // the SRP server.
+ optional uint64 key_lease_time_total_ms = 4;
+
+ // The sum of remaining lease time in milliseconds of all active
+ // hosts/services on the SRP server.
+ optional uint64 remaining_lease_time_total_ms = 5;
+
+ // The sum of remaining key lease time in milliseconds of all active
+ // hosts/services on the SRP server.
+ optional uint64 remaining_key_lease_time_total_ms = 6;
+ }
+
+ message SrpServerResponseCounters {
+ // The number of successful responses
+ optional uint32 success_count = 1;
+
+ // The number of server failure responses
+ optional uint32 server_failure_count = 2;
+
+ // The number of format error responses
+ optional uint32 format_error_count = 3;
+
+ // The number of 'name exists' responses
+ optional uint32 name_exists_count = 4;
+
+ // The number of refused responses
+ optional uint32 refused_count = 5;
+
+ // The number of other responses
+ optional uint32 other_count = 6;
+ }
+
+ enum SrpServerState {
+ SRP_SERVER_STATE_UNSPECIFIED = 0;
+ SRP_SERVER_STATE_DISABLED = 1;
+ SRP_SERVER_STATE_RUNNING = 2;
+ SRP_SERVER_STATE_STOPPED = 3;
+ }
+
+ // The address mode used by the SRP server
+ enum SrpServerAddressMode {
+ SRP_SERVER_ADDRESS_MODE_UNSPECIFIED = 0;
+ SRP_SERVER_ADDRESS_MODE_UNICAST = 1;
+ SRP_SERVER_ADDRESS_MODE_STATE_ANYCAST = 2;
+ }
+
+ message SrpServerInfo {
+ // The state of the SRP server
+ optional SrpServerState state = 1;
+
+ // Listening port number
+ optional uint32 port = 2;
+ // The address mode {unicast, anycast} of the SRP server
+ optional SrpServerAddressMode address_mode = 3;
+
+ // The registration information of hosts on the SRP server
+ optional SrpServerRegistrationInfo hosts = 4;
+
+ // The registration information of services on the SRP server
+ optional SrpServerRegistrationInfo services = 5;
+
+ // The counters of response codes sent by the SRP server
+ optional SrpServerResponseCounters response_counters = 6;
+ }
+
+ message DnsServerResponseCounters {
+ // The number of successful responses
+ optional uint32 success_count = 1;
+
+ // The number of server failure responses
+ optional uint32 server_failure_count = 2;
+
+ // The number of format error responses
+ optional uint32 format_error_count = 3;
+
+ // The number of name error responses
+ optional uint32 name_error_count = 4;
+
+ // The number of 'not implemented' responses
+ optional uint32 not_implemented_count = 5;
+
+ // The number of other responses
+ optional uint32 other_count = 6;
+ }
+
+ message DnsServerInfo {
+ // The counters of response codes sent by the DNS server
+ optional DnsServerResponseCounters response_counters = 1;
+
+ // The number of DNS queries resolved at the local SRP server
+ optional uint32 resolved_by_local_srp_count = 2;
+ }
+
+ message MdnsResponseCounters {
+ // The number of successful responses
+ optional uint32 success_count = 1;
+
+ // The number of 'not found' responses
+ optional uint32 not_found_count = 2;
+
+ // The number of 'invalid arg' responses
+ optional uint32 invalid_args_count = 3;
+
+ // The number of 'duplicated' responses
+ optional uint32 duplicated_count = 4;
+
+ // The number of 'not implemented' responses
+ optional uint32 not_implemented_count = 5;
+
+ // The number of unknown error responses
+ optional uint32 unknown_error_count = 6;
+
+ // The number of aborted responses
+ optional uint32 aborted_count = 7;
+
+ // The number of invalid state responses
+ optional uint32 invalid_state_count = 8;
+ }
+
+ message MdnsInfo {
+ // The response counters of host registrations
+ optional MdnsResponseCounters host_registration_responses = 1;
+
+ // The response counters of service registrations
+ optional MdnsResponseCounters service_registration_responses = 2;
+
+ // The response counters of host resolutions
+ optional MdnsResponseCounters host_resolution_responses = 3;
+
+ // The response counters of service resolutions
+ optional MdnsResponseCounters service_resolution_responses = 4;
+
+ // The EMA (Exponential Moving Average) latencies of mDNS operations
+
+ // The EMA latency of host registrations in milliseconds
+ optional uint32 host_registration_ema_latency_ms = 5;
+
+ // The EMA latency of service registrations in milliseconds
+ optional uint32 service_registration_ema_latency_ms = 6;
+
+ // The EMA latency of host resolutions in milliseconds
+ optional uint32 host_resolution_ema_latency_ms = 7;
+
+ // The EMA latency of service resolutions in milliseconds
+ optional uint32 service_resolution_ema_latency_ms = 8;
+ }
+
+ enum Nat64State {
+ NAT64_STATE_UNSPECIFIED = 0;
+ NAT64_STATE_DISABLED = 1;
+ NAT64_STATE_NOT_RUNNING = 2;
+ NAT64_STATE_IDLE = 3;
+ NAT64_STATE_ACTIVE = 4;
+ }
+
+ message BorderRoutingNat64State {
+ optional Nat64State prefix_manager_state = 1;
+ optional Nat64State translator_state = 2;
+ }
+
+ message Nat64Mapping {
+ optional uint64 mapping_id = 1;
+ optional bytes hashed_ipv6_address = 2;
+ optional Nat64ProtocolCounters counters = 3;
+ }
+
+ message WpanBorderRouter {
+ // Border routing counters
+ optional BorderRoutingCounters border_routing_counters = 1;
+
+ // Information about the SRP server
+ optional SrpServerInfo srp_server = 2;
+
+ // Information about the DNS server
+ optional DnsServerInfo dns_server = 3;
+
+ // Information about the mDNS publisher
+ optional MdnsInfo mdns = 4;
+
+ // TODO(b/285457467): remove this reserved proto field.
+ reserved 5;
+
+ // Information about the state of components of NAT64
+ optional BorderRoutingNat64State nat64_state = 6;
+
+ // Information about the mappings of NAT64 translator
+ repeated Nat64Mapping nat64_mappings = 7;
+ }
+
+ message RcpStabilityStatistics {
+ optional uint32 rcp_timeout_count = 1;
+ optional uint32 rcp_reset_count = 2;
+ optional uint32 rcp_restoration_count = 3;
+ optional uint32 spinel_parse_error_count = 4;
+ optional int32 rcp_firmware_update_count = 5;
+ optional uint32 thread_stack_uptime = 6;
+ }
+
+ message RcpInterfaceStatistics {
+ optional uint32 rcp_interface_type = 1;
+ optional uint64 transferred_frames_count = 2;
+ optional uint64 transferred_valid_frames_count = 3;
+ optional uint64 transferred_garbage_frames_count = 4;
+ optional uint64 rx_frames_count = 5;
+ optional uint64 rx_bytes_count = 6;
+ optional uint64 tx_frames_count = 7;
+ optional uint64 tx_bytes_count = 8;
+ }
+
+ message WpanRcp {
+ optional RcpStabilityStatistics rcp_stability_statistics = 1;
+ optional RcpInterfaceStatistics rcp_interface_statistics = 2;
+ }
+
+ message CoexMetrics {
+ // Use uint32 instead of int64 to save space for payload, and align with the
+ // raw data size.
+ optional uint32 count_tx_request = 1;
+ optional uint32 count_tx_grant_immediate = 2;
+ optional uint32 count_tx_grant_wait = 3;
+ optional uint32 count_tx_grant_wait_activated = 4;
+ optional uint32 count_tx_grant_wait_timeout = 5;
+ optional uint32 count_tx_grant_deactivated_during_request = 6;
+ optional uint32 tx_average_request_to_grant_time_us = 7;
+ optional uint32 count_rx_request = 8;
+ optional uint32 count_rx_grant_immediate = 9;
+ optional uint32 count_rx_grant_wait = 10;
+ optional uint32 count_rx_grant_wait_activated = 11;
+ optional uint32 count_rx_grant_wait_timeout = 12;
+ optional uint32 count_rx_grant_deactivated_during_request = 13;
+ optional uint32 count_rx_grant_none = 14;
+ optional uint32 rx_average_request_to_grant_time_us = 15;
+ }
+
+ optional WpanStats wpan_stats = 1;
+ optional WpanTopoFull wpan_topo_full = 2;
+ repeated TopoEntry topo_entries = 3;
+ optional WpanBorderRouter wpan_border_router = 4;
+ optional WpanRcp wpan_rcp = 5;
+ // Deprecate CoexMetrics with int64 as its fields types to save storage.
+ reserved 6;
+ optional CoexMetrics coex_metrics = 7;
+}
diff --git a/src/rest/connection.hpp b/src/rest/connection.hpp
index 93407c30..728c2b7e 100644
--- a/src/rest/connection.hpp
+++ b/src/rest/connection.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_REST_CONNECTION_HPP_
#define OTBR_REST_CONNECTION_HPP_
+#include "openthread-br/config.h"
+
#include <string.h>
#include <unistd.h>
diff --git a/src/rest/json.cpp b/src/rest/json.cpp
index 8aeb89a4..d10db85d 100644
--- a/src/rest/json.cpp
+++ b/src/rest/json.cpp
@@ -73,6 +73,22 @@ exit:
return ret;
}
+bool JsonString2String(const std::string &aJsonString, std::string &aString)
+{
+ cJSON *jsonString;
+ bool ret = true;
+
+ VerifyOrExit((jsonString = cJSON_Parse(aJsonString.c_str())) != nullptr, ret = false);
+ VerifyOrExit(cJSON_IsString(jsonString), ret = false);
+
+ aString = std::string(jsonString->valuestring);
+
+exit:
+ cJSON_Delete(jsonString);
+
+ return ret;
+}
+
std::string Json2String(const cJSON *aJson)
{
std::string ret;
@@ -346,8 +362,8 @@ std::string Node2JsonString(const NodeInfo &aNode)
cJSON *node = cJSON_CreateObject();
std::string ret;
- cJSON_AddItemToObject(node, "BaId", Bytes2HexJson(aNode.mBaId, OT_BORDER_AGENT_ID_LENGTH));
- cJSON_AddItemToObject(node, "State", cJSON_CreateNumber(aNode.mRole));
+ cJSON_AddItemToObject(node, "BaId", Bytes2HexJson(aNode.mBaId.mId, sizeof(aNode.mBaId)));
+ cJSON_AddItemToObject(node, "State", cJSON_CreateString(aNode.mRole.c_str()));
cJSON_AddItemToObject(node, "NumOfRouter", cJSON_CreateNumber(aNode.mNumOfRouter));
cJSON_AddItemToObject(node, "RlocAddress", IpAddr2Json(aNode.mRlocAddress));
cJSON_AddItemToObject(node, "ExtAddress", Bytes2HexJson(aNode.mExtAddress, OT_EXT_ADDRESS_SIZE));
diff --git a/src/rest/json.hpp b/src/rest/json.hpp
index 29153d51..7df74db5 100644
--- a/src/rest/json.hpp
+++ b/src/rest/json.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_REST_JSON_HPP_
#define OTBR_REST_JSON_HPP_
+#include "openthread-br/config.h"
+
#include "openthread/dataset.h"
#include "openthread/link.h"
#include "openthread/thread_ftd.h"
@@ -104,6 +106,16 @@ std::string CString2JsonString(const char *aCString);
std::string String2JsonString(const std::string &aString);
/**
+ * This method parses a Json string and checks its datatype and returns a string if it is a string.
+ *
+ * @param[in] aJsonString A Json string.
+ * @param[out] aString The string.
+ *
+ * @returns A boolean indicating whether the Json string was indeed a string.
+ */
+bool JsonString2String(const std::string &aJsonString, std::string &aString);
+
+/**
* This method formats a Node object to a Json object and serialize it to a string.
*
* @param[in] aNode A Node object.
diff --git a/src/rest/openapi.yaml b/src/rest/openapi.yaml
index f3a4f446..2ba2a4dd 100644
--- a/src/rest/openapi.yaml
+++ b/src/rest/openapi.yaml
@@ -43,6 +43,15 @@ paths:
application/json:
schema:
type: object
+ delete:
+ tags:
+ - node
+ summary: Erase all persistent information, essentially factory reset the Border Router.
+ responses:
+ "200":
+ description: Successful operation
+ "409":
+ description: Thread interface is in wrong state.
/node/ba-id:
get:
tags:
@@ -107,20 +116,38 @@ paths:
summary: Get current Thread state.
description: |-
State describing the current Thread role of this Thread node.
- - 0: disabled
- - 1: detached
- - 2: child
- - 3: router
- - 4: leader
+ - disabled: The Thread stack is disabled.
+ - detached: Not currently participating in a Thread network/partition.
+ - child: The Thread Child role.
+ - router: The Thread Router role.
+ - leader: The Thread Leader role.
responses:
"200":
description: Successful operation
content:
application/json:
schema:
- type: number
+ type: string
description: Current state
- example: 4
+ example: "leader"
+ put:
+ tags:
+ - node
+ summary: Set current Thread state.
+ description: |-
+ Enable and disable the Thread protocol operation. If network interface
+ hasn't been started yet, it will get started automatically.
+ responses:
+ "200":
+ description: Successful operation.
+ requestBody:
+ description: New Thread state
+ content:
+ application/json:
+ schema:
+ type: string
+ description: Can be "enable" or "disable".
+ example: "enable"
/node/network-name:
get:
tags:
diff --git a/src/rest/parser.hpp b/src/rest/parser.hpp
index a90ad759..b79d79a8 100644
--- a/src/rest/parser.hpp
+++ b/src/rest/parser.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_REST_PARSER_HPP_
#define OTBR_REST_PARSER_HPP_
+#include "openthread-br/config.h"
+
#include <memory>
#include "rest/types.hpp"
diff --git a/src/rest/request.hpp b/src/rest/request.hpp
index 5b73b067..ceb92a5f 100644
--- a/src/rest/request.hpp
+++ b/src/rest/request.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_REST_REQUEST_HPP_
#define OTBR_REST_REQUEST_HPP_
+#include "openthread-br/config.h"
+
#include <map>
#include <string>
diff --git a/src/rest/resource.cpp b/src/rest/resource.cpp
index 4c8d2573..a60e9d94 100644
--- a/src/rest/resource.cpp
+++ b/src/rest/resource.cpp
@@ -30,8 +30,6 @@
#include "rest/resource.hpp"
-#include "string.h"
-
#define OT_PSKC_MAX_LENGTH 16
#define OT_EXTENDED_PANID_LENGTH 8
@@ -222,9 +220,8 @@ void Resource::GetNodeInfo(Response &aResponse) const
uint8_t maxRouterId;
std::string body;
std::string errorCode;
- uint16_t idLength = OT_BORDER_AGENT_ID_LENGTH;
- VerifyOrExit(otBorderAgentGetId(mInstance, node.mBaId, &idLength) == OT_ERROR_NONE, error = OTBR_ERROR_REST);
+ VerifyOrExit(otBorderAgentGetId(mInstance, &node.mBaId) == OT_ERROR_NONE, error = OTBR_ERROR_REST);
(void)otThreadGetLeaderData(mInstance, &node.mLeaderData);
node.mNumOfRouter = 0;
@@ -238,7 +235,7 @@ void Resource::GetNodeInfo(Response &aResponse) const
++node.mNumOfRouter;
}
- node.mRole = otThreadGetDeviceRole(mInstance);
+ node.mRole = GetDeviceRoleName(otThreadGetDeviceRole(mInstance));
node.mExtAddress = reinterpret_cast<const uint8_t *>(otLinkGetExtendedAddress(mInstance));
node.mNetworkName = otThreadGetNetworkName(mInstance);
node.mRloc16 = otThreadGetRloc16(mInstance);
@@ -260,30 +257,59 @@ exit:
}
}
-void Resource::NodeInfo(const Request &aRequest, Response &aResponse) const
+void Resource::DeleteNodeInfo(Response &aResponse) const
{
+ otbrError error = OTBR_ERROR_NONE;
std::string errorCode;
- if (aRequest.GetMethod() == HttpMethod::kGet)
+
+ VerifyOrExit(mNcp->GetThreadHelper()->Detach() == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE);
+ VerifyOrExit(otInstanceErasePersistentInfo(mInstance) == OT_ERROR_NONE, error = OTBR_ERROR_REST);
+ mNcp->Reset();
+
+exit:
+ if (error == OTBR_ERROR_NONE)
{
- GetNodeInfo(aResponse);
+ errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
+ aResponse.SetResponsCode(errorCode);
}
- else
+ else if (error == OTBR_ERROR_INVALID_STATE)
+ {
+ ErrorHandler(aResponse, HttpStatusCode::kStatusConflict);
+ }
+ else if (error != OTBR_ERROR_NONE)
+ {
+ ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError);
+ }
+}
+
+void Resource::NodeInfo(const Request &aRequest, Response &aResponse) const
+{
+ std::string errorCode;
+
+ switch (aRequest.GetMethod())
{
+ case HttpMethod::kGet:
+ GetNodeInfo(aResponse);
+ break;
+ case HttpMethod::kDelete:
+ DeleteNodeInfo(aResponse);
+ break;
+ default:
ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
+ break;
}
}
void Resource::GetDataBaId(Response &aResponse) const
{
- otbrError error = OTBR_ERROR_NONE;
- uint8_t id[OT_BORDER_AGENT_ID_LENGTH];
- uint16_t idLength = OT_BORDER_AGENT_ID_LENGTH;
- std::string body;
- std::string errorCode;
+ otbrError error = OTBR_ERROR_NONE;
+ otBorderAgentId id;
+ std::string body;
+ std::string errorCode;
- VerifyOrExit(otBorderAgentGetId(mInstance, id, &idLength) == OT_ERROR_NONE, error = OTBR_ERROR_REST);
+ VerifyOrExit(otBorderAgentGetId(mInstance, &id) == OT_ERROR_NONE, error = OTBR_ERROR_REST);
- body = Json::Bytes2HexJsonString(id, idLength);
+ body = Json::Bytes2HexJsonString(id.mId, sizeof(id));
aResponse.SetBody(body);
exit:
@@ -339,33 +365,80 @@ void Resource::ExtendedAddr(const Request &aRequest, Response &aResponse) const
void Resource::GetDataState(Response &aResponse) const
{
- std::string state;
- std::string errorCode;
- uint8_t role;
- // 0 : disabled
- // 1 : detached
- // 2 : child
- // 3 : router
- // 4 : leader
+ std::string state;
+ std::string errorCode;
+ otDeviceRole role;
role = otThreadGetDeviceRole(mInstance);
- state = Json::Number2JsonString(role);
+ state = Json::String2JsonString(GetDeviceRoleName(role));
aResponse.SetBody(state);
errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
aResponse.SetResponsCode(errorCode);
}
-void Resource::State(const Request &aRequest, Response &aResponse) const
+void Resource::SetDataState(const Request &aRequest, Response &aResponse) const
{
+ otbrError error = OTBR_ERROR_NONE;
std::string errorCode;
+ std::string body;
- if (aRequest.GetMethod() == HttpMethod::kGet)
+ VerifyOrExit(Json::JsonString2String(aRequest.GetBody(), body), error = OTBR_ERROR_INVALID_ARGS);
+ if (body == "enable")
{
- GetDataState(aResponse);
+ if (!otIp6IsEnabled(mInstance))
+ {
+ VerifyOrExit(otIp6SetEnabled(mInstance, true) == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE);
+ }
+ VerifyOrExit(otThreadSetEnabled(mInstance, true) == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE);
+ }
+ else if (body == "disable")
+ {
+ VerifyOrExit(otThreadSetEnabled(mInstance, false) == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE);
+ VerifyOrExit(otIp6SetEnabled(mInstance, false) == OT_ERROR_NONE, error = OTBR_ERROR_INVALID_STATE);
}
else
{
+ ExitNow(error = OTBR_ERROR_INVALID_ARGS);
+ }
+
+ errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
+ aResponse.SetResponsCode(errorCode);
+
+exit:
+ if (error == OTBR_ERROR_INVALID_STATE)
+ {
+ ErrorHandler(aResponse, HttpStatusCode::kStatusConflict);
+ }
+ if (error == OTBR_ERROR_INVALID_ARGS)
+ {
+ ErrorHandler(aResponse, HttpStatusCode::kStatusBadRequest);
+ }
+ else if (error != OTBR_ERROR_NONE)
+ {
+ ErrorHandler(aResponse, HttpStatusCode::kStatusInternalServerError);
+ }
+}
+
+void Resource::State(const Request &aRequest, Response &aResponse) const
+{
+ std::string errorCode;
+
+ switch (aRequest.GetMethod())
+ {
+ case HttpMethod::kGet:
+ GetDataState(aResponse);
+ break;
+ case HttpMethod::kPut:
+ SetDataState(aRequest, aResponse);
+ break;
+ case HttpMethod::kOptions:
+ errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
+ aResponse.SetResponsCode(errorCode);
+ aResponse.SetComplete();
+ break;
+ default:
ErrorHandler(aResponse, HttpStatusCode::kStatusMethodNotAllowed);
+ break;
}
}
@@ -617,7 +690,7 @@ void Resource::SetDataset(DatasetType aDatasetType, const Request &aRequest, Res
struct NodeInfo node;
std::string body;
std::string errorCode = GetHttpStatus(HttpStatusCode::kStatusOk);
- otOperationalDataset dataset;
+ otOperationalDataset dataset = {};
otOperationalDatasetTlvs datasetTlvs;
otOperationalDatasetTlvs datasetUpdateTlvs;
int ret;
diff --git a/src/rest/resource.hpp b/src/rest/resource.hpp
index 7b5e592a..d79085db 100644
--- a/src/rest/resource.hpp
+++ b/src/rest/resource.hpp
@@ -34,11 +34,14 @@
#ifndef OTBR_REST_RESOURCE_HPP_
#define OTBR_REST_RESOURCE_HPP_
+#include "openthread-br/config.h"
+
#include <unordered_map>
#include <openthread/border_agent.h>
#include <openthread/border_router.h>
+#include "common/api_strings.hpp"
#include "ncp/ncp_openthread.hpp"
#include "openthread/dataset.h"
#include "openthread/dataset_ftd.h"
@@ -134,9 +137,11 @@ private:
void HandleDiagnosticCallback(const Request &aRequest, Response &aResponse);
void GetNodeInfo(Response &aResponse) const;
+ void DeleteNodeInfo(Response &aResponse) const;
void GetDataBaId(Response &aResponse) const;
void GetDataExtendedAddr(Response &aResponse) const;
void GetDataState(Response &aResponse) const;
+ void SetDataState(const Request &aRequest, Response &aResponse) const;
void GetDataNetworkName(Response &aResponse) const;
void GetDataLeaderData(Response &aResponse) const;
void GetDataNumOfRoute(Response &aResponse) const;
diff --git a/src/rest/response.cpp b/src/rest/response.cpp
index 93cbe0b6..3460b90e 100644
--- a/src/rest/response.cpp
+++ b/src/rest/response.cpp
@@ -34,7 +34,7 @@
#define OT_REST_RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS \
"Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, " \
"Access-Control-Request-Headers"
-#define OT_REST_RESPONSE_ACCESS_CONTROL_ALLOW_METHOD "GET, OPTIONS, PUT"
+#define OT_REST_RESPONSE_ACCESS_CONTROL_ALLOW_METHOD "DELETE, GET, OPTIONS, PUT"
#define OT_REST_RESPONSE_CONNECTION "close"
namespace otbr {
diff --git a/src/rest/response.hpp b/src/rest/response.hpp
index a7cb73e3..2bab14c6 100644
--- a/src/rest/response.hpp
+++ b/src/rest/response.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_REST_RESPONSE_HPP_
#define OTBR_REST_RESPONSE_HPP_
+#include "openthread-br/config.h"
+
#include <chrono>
#include <map>
#include <string>
diff --git a/src/rest/rest_web_server.hpp b/src/rest/rest_web_server.hpp
index 6e7956ba..1da2e013 100644
--- a/src/rest/rest_web_server.hpp
+++ b/src/rest/rest_web_server.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_REST_REST_WEB_SERVER_HPP_
#define OTBR_REST_REST_WEB_SERVER_HPP_
+#include "openthread-br/config.h"
+
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/socket.h>
diff --git a/src/rest/types.hpp b/src/rest/types.hpp
index 0723661a..aaa11d1b 100644
--- a/src/rest/types.hpp
+++ b/src/rest/types.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_REST_TYPES_HPP_
#define OTBR_REST_TYPES_HPP_
+#include "openthread-br/config.h"
+
#include <chrono>
#include <string>
#include <vector>
@@ -98,15 +100,15 @@ enum class ConnectionState : std::uint8_t
};
struct NodeInfo
{
- uint8_t mBaId[OT_BORDER_AGENT_ID_LENGTH];
- uint32_t mRole;
- uint32_t mNumOfRouter;
- uint16_t mRloc16;
- const uint8_t *mExtPanId;
- const uint8_t *mExtAddress;
- otIp6Address mRlocAddress;
- otLeaderData mLeaderData;
- std::string mNetworkName;
+ otBorderAgentId mBaId;
+ std::string mRole;
+ uint32_t mNumOfRouter;
+ uint16_t mRloc16;
+ const uint8_t *mExtPanId;
+ const uint8_t *mExtAddress;
+ otIp6Address mRlocAddress;
+ otLeaderData mLeaderData;
+ std::string mNetworkName;
};
struct DiagInfo
diff --git a/src/sdp_proxy/advertising_proxy.cpp b/src/sdp_proxy/advertising_proxy.cpp
index d254ed0d..7eac4d0c 100644
--- a/src/sdp_proxy/advertising_proxy.cpp
+++ b/src/sdp_proxy/advertising_proxy.cpp
@@ -247,18 +247,16 @@ otbrError AdvertisingProxy::PublishHostAndItsServices(const otSrpServerHost *aHo
aUpdate->mCallbackCount++;
aUpdate->mHostName = hostName;
service = nullptr;
- while ((service = otSrpServerHostFindNextService(aHost, service, OT_SRP_SERVER_FLAGS_BASE_TYPE_SERVICE_ONLY,
- /* aServiceName */ nullptr, /* aInstanceName */ nullptr)))
+ while ((service = otSrpServerHostGetNextService(aHost, service)) != nullptr)
{
aUpdate->mCallbackCount++;
}
}
service = nullptr;
- while ((service = otSrpServerHostFindNextService(aHost, service, OT_SRP_SERVER_FLAGS_BASE_TYPE_SERVICE_ONLY,
- /* aServiceName */ nullptr, /* aInstanceName */ nullptr)))
+ while ((service = otSrpServerHostGetNextService(aHost, service)) != nullptr)
{
- std::string fullServiceName = otSrpServerServiceGetFullName(service);
+ std::string fullServiceName = otSrpServerServiceGetInstanceName(service);
std::string serviceName;
std::string serviceType;
std::string serviceDomain;
@@ -267,12 +265,12 @@ otbrError AdvertisingProxy::PublishHostAndItsServices(const otSrpServerHost *aHo
if (!hostDeleted && !otSrpServerServiceIsDeleted(service))
{
- Mdns::Publisher::TxtList txtList = MakeTxtList(service);
+ Mdns::Publisher::TxtData txtData = MakeTxtData(service);
Mdns::Publisher::SubTypeList subTypeList = MakeSubTypeList(service);
otbrLogDebug("Publish SRP service '%s'", fullServiceName.c_str());
mPublisher.PublishService(
- hostName, serviceName, serviceType, subTypeList, otSrpServerServiceGetPort(service), txtList,
+ hostName, serviceName, serviceType, subTypeList, otSrpServerServiceGetPort(service), txtData,
[this, hasUpdate, updateId, fullServiceName](otbrError aError) {
otbrLogResult(aError, "Handle publish SRP service '%s'", fullServiceName.c_str());
if (hasUpdate)
@@ -340,49 +338,31 @@ exit:
return error;
}
-Mdns::Publisher::TxtList AdvertisingProxy::MakeTxtList(const otSrpServerService *aSrpService)
+Mdns::Publisher::TxtData AdvertisingProxy::MakeTxtData(const otSrpServerService *aSrpService)
{
- const uint8_t *txtData;
- uint16_t txtDataLength = 0;
- otDnsTxtEntryIterator iterator;
- otDnsTxtEntry txtEntry;
- Mdns::Publisher::TxtList txtList;
+ const uint8_t *data;
+ uint16_t length = 0;
- txtData = otSrpServerServiceGetTxtData(aSrpService, &txtDataLength);
+ data = otSrpServerServiceGetTxtData(aSrpService, &length);
- otDnsInitTxtEntryIterator(&iterator, txtData, txtDataLength);
-
- while (otDnsGetNextTxtEntry(&iterator, &txtEntry) == OT_ERROR_NONE)
- {
- txtList.emplace_back(txtEntry.mKey, txtEntry.mValue, txtEntry.mValueLength);
- }
-
- return txtList;
+ return Mdns::Publisher::TxtData(data, data + length);
}
Mdns::Publisher::SubTypeList AdvertisingProxy::MakeSubTypeList(const otSrpServerService *aSrpService)
{
- const otSrpServerHost *host = otSrpServerServiceGetHost(aSrpService);
- const char *instanceName = otSrpServerServiceGetInstanceName(aSrpService);
- const otSrpServerService *subService = nullptr;
Mdns::Publisher::SubTypeList subTypeList;
- while ((subService = otSrpServerHostFindNextService(
- host, subService, (OT_SRP_SERVER_SERVICE_FLAG_SUB_TYPE | OT_SRP_SERVER_SERVICE_FLAG_ACTIVE),
- /* aServiceName */ nullptr, instanceName)) != nullptr)
+ for (uint16_t index = 0;; index++)
{
- char subLabel[OT_DNS_MAX_LABEL_SIZE];
+ const char *subTypeName = otSrpServerServiceGetSubTypeServiceNameAt(aSrpService, index);
+ char subLabel[OT_DNS_MAX_LABEL_SIZE];
- if (otSrpServerServiceGetServiceSubTypeLabel(subService, subLabel, sizeof(subLabel)) == OT_ERROR_NONE)
- {
- subTypeList.emplace_back(subLabel);
- }
- else
- {
- otbrLogWarning("Failed to retrieve subtype of SRP service: %s", otSrpServerServiceGetFullName(aSrpService));
- }
+ VerifyOrExit(subTypeName != nullptr);
+ SuccessOrExit(otSrpServerParseSubTypeServiceName(subTypeName, subLabel, sizeof(subLabel)));
+ subTypeList.emplace_back(subLabel);
}
+exit:
return subTypeList;
}
diff --git a/src/sdp_proxy/advertising_proxy.hpp b/src/sdp_proxy/advertising_proxy.hpp
index e07a7d2e..385dadd6 100644
--- a/src/sdp_proxy/advertising_proxy.hpp
+++ b/src/sdp_proxy/advertising_proxy.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_SRP_ADVERTISING_PROXY_HPP_
#define OTBR_SRP_ADVERTISING_PROXY_HPP_
+#include "openthread-br/config.h"
+
#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
#include <stdint.h>
@@ -98,7 +100,7 @@ private:
void *aContext);
void AdvertisingHandler(otSrpServerServiceUpdateId aId, const otSrpServerHost *aHost, uint32_t aTimeout);
- static Mdns::Publisher::TxtList MakeTxtList(const otSrpServerService *aSrpService);
+ static Mdns::Publisher::TxtData MakeTxtData(const otSrpServerService *aSrpService);
static Mdns::Publisher::SubTypeList MakeSubTypeList(const otSrpServerService *aSrpService);
void OnMdnsPublishResult(otSrpServerServiceUpdateId aUpdateId, otbrError aError);
@@ -129,9 +131,6 @@ private:
// A vector that tracks outstanding updates.
std::vector<OutstandingUpdate> mOutstandingUpdates;
-
- // Task runner for running tasks in the context of the main thread.
- TaskRunner mTaskRunner;
};
} // namespace otbr
diff --git a/src/sdp_proxy/discovery_proxy.hpp b/src/sdp_proxy/discovery_proxy.hpp
index 5a078acd..5263ab1c 100644
--- a/src/sdp_proxy/discovery_proxy.hpp
+++ b/src/sdp_proxy/discovery_proxy.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_AGENT_DISCOVERY_PROXY_HPP_
#define OTBR_AGENT_DISCOVERY_PROXY_HPP_
+#include "openthread-br/config.h"
+
#if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
#include <set>
diff --git a/src/trel_dnssd/trel_dnssd.cpp b/src/trel_dnssd/trel_dnssd.cpp
index 11794fcb..c285a834 100644
--- a/src/trel_dnssd/trel_dnssd.cpp
+++ b/src/trel_dnssd/trel_dnssd.cpp
@@ -249,7 +249,7 @@ void TrelDnssd::PublishTrelService(void)
mRegisterInfo.mInstanceName = GetTrelInstanceName();
mPublisher.PublishService(/* aHostName */ "", mRegisterInfo.mInstanceName, kTrelServiceName,
- Mdns::Publisher::SubTypeList{}, mRegisterInfo.mPort, mRegisterInfo.mTxtEntries,
+ Mdns::Publisher::SubTypeList{}, mRegisterInfo.mPort, mRegisterInfo.mTxtData,
[](otbrError aError) { HandlePublishTrelServiceError(aError); });
}
@@ -461,18 +461,11 @@ uint16_t TrelDnssd::CountDuplicatePeers(const TrelDnssd::Peer &aPeer)
void TrelDnssd::RegisterInfo::Assign(uint16_t aPort, const uint8_t *aTxtData, uint8_t aTxtLength)
{
- otbrError error;
-
- OTBR_UNUSED_VARIABLE(error);
-
assert(!IsPublished());
assert(aPort > 0);
mPort = aPort;
- mTxtEntries.clear();
-
- error = Mdns::Publisher::DecodeTxtData(mTxtEntries, aTxtData, aTxtLength);
- assert(error == OTBR_ERROR_NONE);
+ mTxtData.assign(aTxtData, aTxtData + aTxtLength);
}
void TrelDnssd::RegisterInfo::Clear(void)
@@ -480,7 +473,7 @@ void TrelDnssd::RegisterInfo::Clear(void)
assert(!IsPublished());
mPort = 0;
- mTxtEntries.clear();
+ mTxtData.clear();
}
const char TrelDnssd::Peer::kTxtRecordExtAddressKey[] = "xa";
@@ -495,7 +488,12 @@ void TrelDnssd::Peer::ReadExtAddrFromTxtData(void)
for (const auto &txtEntry : txtEntries)
{
- if (StringUtils::EqualCaseInsensitive(txtEntry.mName, kTxtRecordExtAddressKey))
+ if (txtEntry.mIsBooleanAttribute)
+ {
+ continue;
+ }
+
+ if (StringUtils::EqualCaseInsensitive(txtEntry.mKey, kTxtRecordExtAddressKey))
{
VerifyOrExit(txtEntry.mValue.size() == sizeof(mExtAddr));
diff --git a/src/trel_dnssd/trel_dnssd.hpp b/src/trel_dnssd/trel_dnssd.hpp
index b00eff5e..8f44104e 100644
--- a/src/trel_dnssd/trel_dnssd.hpp
+++ b/src/trel_dnssd/trel_dnssd.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_AGENT_TREL_DNSSD_HPP_
#define OTBR_AGENT_TREL_DNSSD_HPP_
+#include "openthread-br/config.h"
+
#if OTBR_ENABLE_TREL
#include <assert.h>
@@ -118,9 +120,9 @@ private:
struct RegisterInfo
{
- uint16_t mPort = 0;
- std::vector<Mdns::Publisher::TxtEntry> mTxtEntries;
- std::string mInstanceName;
+ uint16_t mPort = 0;
+ Mdns::Publisher::TxtData mTxtData;
+ std::string mInstanceName;
bool IsValid(void) const { return mPort > 0; }
bool IsPublished(void) const { return !mInstanceName.empty(); }
diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt
index 5a624844..4f9c4202 100644
--- a/src/utils/CMakeLists.txt
+++ b/src/utils/CMakeLists.txt
@@ -32,6 +32,7 @@ add_library(otbr-utils
hex.cpp
infra_link_selector.cpp
pskc.cpp
+ sha256.cpp
socket_utils.cpp
steering_data.cpp
string_utils.cpp
@@ -39,7 +40,10 @@ add_library(otbr-utils
thread_helper.cpp
thread_helper.hpp
)
-target_link_libraries(otbr-utils PRIVATE
+
+target_link_libraries(otbr-utils PUBLIC
otbr-common
+ $<$<BOOL:${OTBR_FEATURE_FLAGS}>:otbr-proto>
+ $<$<BOOL:${OTBR_TELEMETRY_DATA_API}>:otbr-proto>
mbedtls
)
diff --git a/src/utils/infra_link_selector.hpp b/src/utils/infra_link_selector.hpp
index 02a231b1..f2bcd832 100644
--- a/src/utils/infra_link_selector.hpp
+++ b/src/utils/infra_link_selector.hpp
@@ -34,6 +34,8 @@
#ifndef INFRA_LINK_SELECTOR_HPP_
#define INFRA_LINK_SELECTOR_HPP_
+#include "openthread-br/config.h"
+
#if __linux__
#include <assert.h>
diff --git a/src/utils/sha256.cpp b/src/utils/sha256.cpp
new file mode 100644
index 00000000..30be250e
--- /dev/null
+++ b/src/utils/sha256.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2023, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * This file implements SHA-256.
+ */
+
+#include "sha256.hpp"
+
+namespace otbr {
+
+Sha256::Sha256(void)
+{
+ otError error;
+
+ mContext.mContext = &mContextStorage;
+ mContext.mContextSize = sizeof(mContextStorage);
+
+ SuccessOrExit(error = otPlatCryptoSha256Init(&mContext));
+
+exit:
+ if (error != OT_ERROR_NONE)
+ {
+ otbrLogErr("Error otPlatCryptoSha256Init: %s", otThreadErrorToString(error));
+ }
+}
+
+Sha256::~Sha256(void)
+{
+ otError error;
+
+ SuccessOrExit(error = otPlatCryptoSha256Deinit(&mContext));
+
+exit:
+ if (error != OT_ERROR_NONE)
+ {
+ otbrLogErr("Error otPlatCryptoSha256Deinit: %s", otThreadErrorToString(error));
+ }
+}
+
+void Sha256::Start(void)
+{
+ otError error;
+
+ SuccessOrExit(error = otPlatCryptoSha256Start(&mContext));
+
+exit:
+ if (error != OT_ERROR_NONE)
+ {
+ otbrLogErr("Error otPlatCryptoSha256Start: %s", otThreadErrorToString(error));
+ }
+}
+
+void Sha256::Update(const void *aBuf, uint16_t aBufLength)
+{
+ otError error;
+
+ SuccessOrExit(error = otPlatCryptoSha256Update(&mContext, aBuf, aBufLength));
+
+exit:
+ if (error != OT_ERROR_NONE)
+ {
+ otbrLogErr("Error otPlatCryptoSha256Update: %s", otThreadErrorToString(error));
+ }
+}
+
+void Sha256::Finish(Hash &aHash)
+{
+ otError error;
+
+ SuccessOrExit(error = otPlatCryptoSha256Finish(&mContext, aHash.m8, Hash::kSize));
+
+exit:
+ if (error != OT_ERROR_NONE)
+ {
+ otbrLogErr("Error otPlatCryptoSha256Finish: %s", otThreadErrorToString(error));
+ }
+}
+} // namespace otbr
diff --git a/src/utils/sha256.hpp b/src/utils/sha256.hpp
new file mode 100644
index 00000000..df90a846
--- /dev/null
+++ b/src/utils/sha256.hpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2023, The OpenThread Authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * This file includes definitions for performing SHA-256 computations.
+ */
+
+#ifndef SHA256_HPP_
+#define SHA256_HPP_
+
+#include <openthread/crypto.h>
+#include <openthread/platform/crypto.h>
+#include "common/code_utils.hpp"
+
+#include <mbedtls/sha256.h>
+
+namespace otbr {
+/**
+ * @addtogroup core-security
+ *
+ * @{
+ *
+ */
+
+/**
+ * This class implements SHA-256 computation.
+ *
+ */
+class Sha256
+{
+public:
+ /**
+ * This type represents a SHA-256 hash.
+ *
+ */
+ class Hash : public otCryptoSha256Hash
+ {
+ public:
+ static const uint8_t kSize = OT_CRYPTO_SHA256_HASH_SIZE; ///< SHA-256 hash size (bytes)
+
+ /**
+ * This method returns a pointer to a byte array containing the hash value.
+ *
+ * @returns A pointer to a byte array containing the hash.
+ *
+ */
+ const uint8_t *GetBytes(void) const { return m8; }
+ };
+
+ /**
+ * Constructor for `Sha256` object.
+ *
+ */
+ Sha256(void);
+
+ /**
+ * Destructor for `Sha256` object.
+ *
+ */
+ ~Sha256(void);
+
+ /**
+ * This method starts the SHA-256 computation.
+ *
+ */
+ void Start(void);
+
+ /**
+ * This method inputs bytes into the SHA-256 computation.
+ *
+ * @param[in] aBuf A pointer to the input buffer.
+ * @param[in] aBufLength The length of @p aBuf in bytes.
+ *
+ */
+ void Update(const void *aBuf, uint16_t aBufLength);
+
+ /**
+ * This method finalizes the hash computation.
+ *
+ * @param[out] aHash A reference to a `Hash` to output the calculated hash.
+ *
+ */
+ void Finish(Hash &aHash);
+
+private:
+ otCryptoContext mContext;
+ const static uint16_t kSha256ContextSize = sizeof(mbedtls_sha256_context);
+ OT_DEFINE_ALIGNED_VAR(mContextStorage, kSha256ContextSize, uint64_t);
+};
+} // namespace otbr
+
+#endif // SHA256_HPP_
diff --git a/src/utils/system_utils.hpp b/src/utils/system_utils.hpp
index 4ae2dad1..2af1ae2e 100644
--- a/src/utils/system_utils.hpp
+++ b/src/utils/system_utils.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_UTILS_SYSTEM_UTILS_HPP_
#define OTBR_UTILS_SYSTEM_UTILS_HPP_
+#include "openthread-br/config.h"
+
namespace otbr {
namespace SystemUtils {
diff --git a/src/utils/thread_helper.cpp b/src/utils/thread_helper.cpp
index 9c69b369..9cd2dd8e 100644
--- a/src/utils/thread_helper.cpp
+++ b/src/utils/thread_helper.cpp
@@ -39,8 +39,20 @@
#include <openthread/border_router.h>
#include <openthread/channel_manager.h>
+#include <openthread/dataset_ftd.h>
+#if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
+#include <openthread/dnssd_server.h>
+#endif
#include <openthread/jam_detection.h>
#include <openthread/joiner.h>
+#if OTBR_ENABLE_NAT64
+#include <openthread/crypto.h>
+#include <openthread/nat64.h>
+#include "utils/sha256.hpp"
+#endif
+#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
+#include <openthread/srp_server.h>
+#endif
#include <openthread/thread_ftd.h>
#include <openthread/platform/radio.h>
@@ -69,12 +81,135 @@ const Tlv *FindTlv(uint8_t aTlvType, const uint8_t *aTlvs, int aTlvsSize)
exit:
return result;
}
+
+#if OTBR_ENABLE_TELEMETRY_DATA_API
+static uint32_t TelemetryNodeTypeFromRoleAndLinkMode(const otDeviceRole &aRole, const otLinkModeConfig &aLinkModeCfg)
+{
+ uint32_t nodeType;
+
+ switch (aRole)
+ {
+ case OT_DEVICE_ROLE_DISABLED:
+ nodeType = threadnetwork::TelemetryData::NODE_TYPE_DISABLED;
+ break;
+ case OT_DEVICE_ROLE_DETACHED:
+ nodeType = threadnetwork::TelemetryData::NODE_TYPE_DETACHED;
+ break;
+ case OT_DEVICE_ROLE_ROUTER:
+ nodeType = threadnetwork::TelemetryData::NODE_TYPE_ROUTER;
+ break;
+ case OT_DEVICE_ROLE_LEADER:
+ nodeType = threadnetwork::TelemetryData::NODE_TYPE_LEADER;
+ break;
+ case OT_DEVICE_ROLE_CHILD:
+ if (!aLinkModeCfg.mRxOnWhenIdle)
+ {
+ nodeType = threadnetwork::TelemetryData::NODE_TYPE_SLEEPY_END;
+ }
+ else if (!aLinkModeCfg.mDeviceType)
+ {
+ // If it's not an FTD, return as minimal end device.
+ nodeType = threadnetwork::TelemetryData::NODE_TYPE_MINIMAL_END;
+ }
+ else
+ {
+ nodeType = threadnetwork::TelemetryData::NODE_TYPE_END;
+ }
+ break;
+ default:
+ nodeType = threadnetwork::TelemetryData::NODE_TYPE_UNSPECIFIED;
+ }
+
+ return nodeType;
+}
+
+#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
+threadnetwork::TelemetryData_SrpServerState SrpServerStateFromOtSrpServerState(otSrpServerState srpServerState)
+{
+ switch (srpServerState)
+ {
+ case OT_SRP_SERVER_STATE_DISABLED:
+ return threadnetwork::TelemetryData::SRP_SERVER_STATE_DISABLED;
+ case OT_SRP_SERVER_STATE_RUNNING:
+ return threadnetwork::TelemetryData::SRP_SERVER_STATE_RUNNING;
+ case OT_SRP_SERVER_STATE_STOPPED:
+ return threadnetwork::TelemetryData::SRP_SERVER_STATE_STOPPED;
+ default:
+ return threadnetwork::TelemetryData::SRP_SERVER_STATE_UNSPECIFIED;
+ }
+}
+
+threadnetwork::TelemetryData_SrpServerAddressMode SrpServerAddressModeFromOtSrpServerAddressMode(
+ otSrpServerAddressMode srpServerAddressMode)
+{
+ switch (srpServerAddressMode)
+ {
+ case OT_SRP_SERVER_ADDRESS_MODE_ANYCAST:
+ return threadnetwork::TelemetryData::SRP_SERVER_ADDRESS_MODE_STATE_ANYCAST;
+ case OT_SRP_SERVER_ADDRESS_MODE_UNICAST:
+ return threadnetwork::TelemetryData::SRP_SERVER_ADDRESS_MODE_UNICAST;
+ default:
+ return threadnetwork::TelemetryData::SRP_SERVER_ADDRESS_MODE_UNSPECIFIED;
+ }
+}
+#endif // OTBR_ENABLE_SRP_ADVERTISING_PROXY
+
+#if OTBR_ENABLE_NAT64
+threadnetwork::TelemetryData_Nat64State Nat64StateFromOtNat64State(otNat64State nat64State)
+{
+ switch (nat64State)
+ {
+ case OT_NAT64_STATE_DISABLED:
+ return threadnetwork::TelemetryData::NAT64_STATE_DISABLED;
+ case OT_NAT64_STATE_NOT_RUNNING:
+ return threadnetwork::TelemetryData::NAT64_STATE_NOT_RUNNING;
+ case OT_NAT64_STATE_IDLE:
+ return threadnetwork::TelemetryData::NAT64_STATE_IDLE;
+ case OT_NAT64_STATE_ACTIVE:
+ return threadnetwork::TelemetryData::NAT64_STATE_ACTIVE;
+ default:
+ return threadnetwork::TelemetryData::NAT64_STATE_UNSPECIFIED;
+ }
+}
+
+void CopyNat64TrafficCounters(const otNat64Counters &from, threadnetwork::TelemetryData_Nat64TrafficCounters *to)
+{
+ to->set_ipv4_to_ipv6_packets(from.m4To6Packets);
+ to->set_ipv4_to_ipv6_bytes(from.m4To6Bytes);
+ to->set_ipv6_to_ipv4_packets(from.m6To4Packets);
+ to->set_ipv6_to_ipv4_bytes(from.m6To4Bytes);
+}
+#endif // OTBR_ENABLE_NAT64
+
+void CopyMdnsResponseCounters(const MdnsResponseCounters &from, threadnetwork::TelemetryData_MdnsResponseCounters *to)
+{
+ to->set_success_count(from.mSuccess);
+ to->set_not_found_count(from.mNotFound);
+ to->set_invalid_args_count(from.mInvalidArgs);
+ to->set_duplicated_count(from.mDuplicated);
+ to->set_not_implemented_count(from.mNotImplemented);
+ to->set_unknown_error_count(from.mUnknownError);
+ to->set_aborted_count(from.mAborted);
+ to->set_invalid_state_count(from.mInvalidState);
+}
+#endif // OTBR_ENABLE_TELEMETRY_DATA_API
} // namespace
ThreadHelper::ThreadHelper(otInstance *aInstance, otbr::Ncp::ControllerOpenThread *aNcp)
: mInstance(aInstance)
, mNcp(aNcp)
{
+#if OTBR_ENABLE_TELEMETRY_DATA_API && OTBR_ENABLE_NAT64
+ otError error;
+
+ SuccessOrExit(error = otPlatCryptoRandomGet(mNat64Ipv6AddressSalt, sizeof(mNat64Ipv6AddressSalt)));
+
+exit:
+ if (error != OT_ERROR_NONE)
+ {
+ otbrLogWarning("Error otPlatCryptoRandomGet: %s", otThreadErrorToString(error));
+ }
+#endif
}
void ThreadHelper::StateChangedCallback(otChangedFlags aFlags)
@@ -321,78 +456,51 @@ void ThreadHelper::Attach(const std::string &aNetworkName,
AttachHandler aHandler)
{
- otError error = OT_ERROR_NONE;
- otExtendedPanId extPanId;
- otNetworkKey networkKey;
- otPskc pskc;
- uint32_t channelMask;
- uint8_t channel;
+ otError error = OT_ERROR_NONE;
+ otOperationalDataset dataset = {};
VerifyOrExit(aHandler != nullptr, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(mAttachHandler == nullptr && mJoinerHandler == nullptr, error = OT_ERROR_INVALID_STATE);
- VerifyOrExit(aNetworkKey.empty() || aNetworkKey.size() == sizeof(networkKey.m8), error = OT_ERROR_INVALID_ARGS);
- VerifyOrExit(aPSKc.empty() || aPSKc.size() == sizeof(pskc.m8), error = OT_ERROR_INVALID_ARGS);
+ VerifyOrExit(aNetworkKey.empty() || aNetworkKey.size() == sizeof(dataset.mNetworkKey.m8),
+ error = OT_ERROR_INVALID_ARGS);
+ VerifyOrExit(aPSKc.empty() || aPSKc.size() == sizeof(dataset.mPskc.m8), error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(aChannelMask != 0, error = OT_ERROR_INVALID_ARGS);
- while (aPanId == UINT16_MAX)
- {
- RandomFill(&aPanId, sizeof(aPanId));
- }
+ SuccessOrExit(error = otDatasetCreateNewNetwork(mInstance, &dataset));
if (aExtPanId != UINT64_MAX)
{
- extPanId = ToOtExtendedPanId(aExtPanId);
- }
- else
- {
- *reinterpret_cast<uint64_t *>(&extPanId) = UINT64_MAX;
-
- while (*reinterpret_cast<uint64_t *>(&extPanId) == UINT64_MAX)
- {
- RandomFill(extPanId.m8, sizeof(extPanId.m8));
- }
+ dataset.mExtendedPanId = ToOtExtendedPanId(aExtPanId);
}
if (!aNetworkKey.empty())
{
- memcpy(networkKey.m8, &aNetworkKey[0], sizeof(networkKey.m8));
+ memcpy(dataset.mNetworkKey.m8, &aNetworkKey[0], sizeof(dataset.mNetworkKey.m8));
}
- else
+
+ if (aPanId != UINT16_MAX)
{
- RandomFill(networkKey.m8, sizeof(networkKey.m8));
+ dataset.mPanId = aPanId;
}
if (!aPSKc.empty())
{
- memcpy(pskc.m8, &aPSKc[0], sizeof(pskc.m8));
- }
- else
- {
- RandomFill(pskc.m8, sizeof(pskc.m8));
+ memcpy(dataset.mPskc.m8, &aPSKc[0], sizeof(dataset.mPskc.m8));
}
- if (!otIp6IsEnabled(mInstance))
- {
- SuccessOrExit(error = otIp6SetEnabled(mInstance, true));
- }
+ SuccessOrExit(error = otNetworkNameFromString(&dataset.mNetworkName, aNetworkName.c_str()));
- SuccessOrExit(error = otThreadSetNetworkName(mInstance, aNetworkName.c_str()));
- SuccessOrExit(error = otLinkSetPanId(mInstance, aPanId));
- SuccessOrExit(error = otThreadSetExtendedPanId(mInstance, &extPanId));
- SuccessOrExit(error = otThreadSetNetworkKey(mInstance, &networkKey));
+ dataset.mChannelMask &= aChannelMask;
+ VerifyOrExit(dataset.mChannelMask != 0, otbrLogWarning("Invalid channel mask"), error = OT_ERROR_INVALID_ARGS);
- channelMask = otPlatRadioGetPreferredChannelMask(mInstance) & aChannelMask;
+ dataset.mChannel = RandomChannelFromChannelMask(dataset.mChannelMask);
- if (channelMask == 0)
+ SuccessOrExit(error = otDatasetSetActive(mInstance, &dataset));
+
+ if (!otIp6IsEnabled(mInstance))
{
- channelMask = otLinkGetSupportedChannelMask(mInstance) & aChannelMask;
+ SuccessOrExit(error = otIp6SetEnabled(mInstance, true));
}
- VerifyOrExit(channelMask != 0, otbrLogWarning("Invalid channel mask"), error = OT_ERROR_INVALID_ARGS);
-
- channel = RandomChannelFromChannelMask(channelMask);
- SuccessOrExit(otLinkSetChannel(mInstance, channel));
-
- SuccessOrExit(error = otThreadSetPskc(mInstance, &pskc));
SuccessOrExit(error = otThreadSetEnabled(mInstance, true));
mAttachDelayMs = 0;
@@ -779,5 +887,505 @@ void ThreadHelper::DetachGracefullyCallback(void)
}
}
+#if OTBR_ENABLE_TELEMETRY_DATA_API
+otError ThreadHelper::RetrieveTelemetryData(Mdns::Publisher *aPublisher, threadnetwork::TelemetryData &telemetryData)
+{
+ otError error = OT_ERROR_NONE;
+
+ // Begin of WpanStats section.
+ auto wpanStats = telemetryData.mutable_wpan_stats();
+
+ {
+ otDeviceRole role = otThreadGetDeviceRole(mInstance);
+ otLinkModeConfig otCfg = otThreadGetLinkMode(mInstance);
+
+ wpanStats->set_node_type(TelemetryNodeTypeFromRoleAndLinkMode(role, otCfg));
+ }
+
+ wpanStats->set_channel(otLinkGetChannel(mInstance));
+
+ {
+ uint16_t ccaFailureRate = otLinkGetCcaFailureRate(mInstance);
+
+ wpanStats->set_mac_cca_fail_rate(static_cast<float>(ccaFailureRate) / 0xffff);
+ }
+
+ {
+ int8_t radioTxPower;
+
+ SuccessOrExit(error = otPlatRadioGetTransmitPower(mInstance, &radioTxPower));
+ wpanStats->set_radio_tx_power(radioTxPower);
+ }
+
+ {
+ const otMacCounters *linkCounters = otLinkGetCounters(mInstance);
+
+ wpanStats->set_phy_rx(linkCounters->mRxTotal);
+ wpanStats->set_phy_tx(linkCounters->mTxTotal);
+ wpanStats->set_mac_unicast_rx(linkCounters->mRxUnicast);
+ wpanStats->set_mac_unicast_tx(linkCounters->mTxUnicast);
+ wpanStats->set_mac_broadcast_rx(linkCounters->mRxBroadcast);
+ wpanStats->set_mac_broadcast_tx(linkCounters->mTxBroadcast);
+ wpanStats->set_mac_tx_ack_req(linkCounters->mTxAckRequested);
+ wpanStats->set_mac_tx_no_ack_req(linkCounters->mTxNoAckRequested);
+ wpanStats->set_mac_tx_acked(linkCounters->mTxAcked);
+ wpanStats->set_mac_tx_data(linkCounters->mTxData);
+ wpanStats->set_mac_tx_data_poll(linkCounters->mTxDataPoll);
+ wpanStats->set_mac_tx_beacon(linkCounters->mTxBeacon);
+ wpanStats->set_mac_tx_beacon_req(linkCounters->mTxBeaconRequest);
+ wpanStats->set_mac_tx_other_pkt(linkCounters->mTxOther);
+ wpanStats->set_mac_tx_retry(linkCounters->mTxRetry);
+ wpanStats->set_mac_rx_data(linkCounters->mRxData);
+ wpanStats->set_mac_rx_data_poll(linkCounters->mRxDataPoll);
+ wpanStats->set_mac_rx_beacon(linkCounters->mRxBeacon);
+ wpanStats->set_mac_rx_beacon_req(linkCounters->mRxBeaconRequest);
+ wpanStats->set_mac_rx_other_pkt(linkCounters->mRxOther);
+ wpanStats->set_mac_rx_filter_whitelist(linkCounters->mRxAddressFiltered);
+ wpanStats->set_mac_rx_filter_dest_addr(linkCounters->mRxDestAddrFiltered);
+ wpanStats->set_mac_tx_fail_cca(linkCounters->mTxErrCca);
+ wpanStats->set_mac_rx_fail_decrypt(linkCounters->mRxErrSec);
+ wpanStats->set_mac_rx_fail_no_frame(linkCounters->mRxErrNoFrame);
+ wpanStats->set_mac_rx_fail_unknown_neighbor(linkCounters->mRxErrUnknownNeighbor);
+ wpanStats->set_mac_rx_fail_invalid_src_addr(linkCounters->mRxErrInvalidSrcAddr);
+ wpanStats->set_mac_rx_fail_fcs(linkCounters->mRxErrFcs);
+ wpanStats->set_mac_rx_fail_other(linkCounters->mRxErrOther);
+ }
+
+ {
+ const otIpCounters *ipCounters = otThreadGetIp6Counters(mInstance);
+
+ wpanStats->set_ip_tx_success(ipCounters->mTxSuccess);
+ wpanStats->set_ip_rx_success(ipCounters->mRxSuccess);
+ wpanStats->set_ip_tx_failure(ipCounters->mTxFailure);
+ wpanStats->set_ip_rx_failure(ipCounters->mRxFailure);
+ }
+ // End of WpanStats section.
+
+ {
+ // Begin of WpanTopoFull section.
+ auto wpanTopoFull = telemetryData.mutable_wpan_topo_full();
+ uint16_t rloc16 = otThreadGetRloc16(mInstance);
+
+ wpanTopoFull->set_rloc16(rloc16);
+
+ otRouterInfo info;
+
+ VerifyOrExit(otThreadGetRouterInfo(mInstance, rloc16, &info) == OT_ERROR_NONE, error = OT_ERROR_INVALID_STATE);
+ wpanTopoFull->set_router_id(info.mRouterId);
+
+ otNeighborInfoIterator iter = OT_NEIGHBOR_INFO_ITERATOR_INIT;
+ otNeighborInfo neighborInfo;
+ std::vector<otNeighborInfo> neighborTable;
+
+ while (otThreadGetNextNeighborInfo(mInstance, &iter, &neighborInfo) == OT_ERROR_NONE)
+ {
+ neighborTable.push_back(neighborInfo);
+ }
+ wpanTopoFull->set_neighbor_table_size(neighborTable.size());
+
+ uint16_t childIndex = 0;
+ otChildInfo childInfo;
+ std::vector<otChildInfo> childTable;
+
+ while (otThreadGetChildInfoByIndex(mInstance, childIndex, &childInfo) == OT_ERROR_NONE)
+ {
+ childTable.push_back(childInfo);
+ childIndex++;
+ }
+ wpanTopoFull->set_child_table_size(childTable.size());
+
+ struct otLeaderData leaderData;
+
+ SuccessOrExit(error = otThreadGetLeaderData(mInstance, &leaderData));
+ wpanTopoFull->set_leader_router_id(leaderData.mLeaderRouterId);
+ wpanTopoFull->set_leader_weight(leaderData.mWeighting);
+ wpanTopoFull->set_network_data_version(leaderData.mDataVersion);
+ wpanTopoFull->set_stable_network_data_version(leaderData.mStableDataVersion);
+
+ uint8_t weight = otThreadGetLocalLeaderWeight(mInstance);
+
+ wpanTopoFull->set_leader_local_weight(weight);
+
+ uint32_t partitionId = otThreadGetPartitionId(mInstance);
+
+ wpanTopoFull->set_partition_id(partitionId);
+
+ static constexpr size_t kNetworkDataMaxSize = 255;
+ {
+ uint8_t data[kNetworkDataMaxSize];
+ uint8_t len = sizeof(data);
+ std::vector<uint8_t> networkData;
+
+ SuccessOrExit(error = otNetDataGet(mInstance, /*stable=*/false, data, &len));
+ networkData = std::vector<uint8_t>(&data[0], &data[len]);
+ wpanTopoFull->set_network_data(std::string(networkData.begin(), networkData.end()));
+ }
+
+ {
+ uint8_t data[kNetworkDataMaxSize];
+ uint8_t len = sizeof(data);
+ std::vector<uint8_t> networkData;
+
+ SuccessOrExit(error = otNetDataGet(mInstance, /*stable=*/true, data, &len));
+ networkData = std::vector<uint8_t>(&data[0], &data[len]);
+ wpanTopoFull->set_stable_network_data(std::string(networkData.begin(), networkData.end()));
+ }
+
+ int8_t rssi = otPlatRadioGetRssi(mInstance);
+
+ wpanTopoFull->set_instant_rssi(rssi);
+
+ const otExtendedPanId *extPanId = otThreadGetExtendedPanId(mInstance);
+ uint64_t extPanIdVal;
+
+ extPanIdVal = ConvertOpenThreadUint64(extPanId->m8);
+ wpanTopoFull->set_extended_pan_id(extPanIdVal);
+ // End of WpanTopoFull section.
+
+ // Begin of TopoEntry section.
+ std::map<uint16_t, const otChildInfo *> childMap;
+
+ for (const otChildInfo &childInfo : childTable)
+ {
+ auto pair = childMap.insert({childInfo.mRloc16, &childInfo});
+ if (!pair.second)
+ {
+ // This shouldn't happen, so log an error. It doesn't matter which
+ // duplicate is kept.
+ otbrLogErr("Children with duplicate RLOC16 found: 0x%04x", static_cast<int>(childInfo.mRloc16));
+ }
+ }
+
+ for (const otNeighborInfo &neighborInfo : neighborTable)
+ {
+ auto topoEntry = telemetryData.add_topo_entries();
+ topoEntry->set_rloc16(neighborInfo.mRloc16);
+ topoEntry->mutable_age()->set_seconds(neighborInfo.mAge);
+ topoEntry->set_link_quality_in(neighborInfo.mLinkQualityIn);
+ topoEntry->set_average_rssi(neighborInfo.mAverageRssi);
+ topoEntry->set_last_rssi(neighborInfo.mLastRssi);
+ topoEntry->set_link_frame_counter(neighborInfo.mLinkFrameCounter);
+ topoEntry->set_mle_frame_counter(neighborInfo.mMleFrameCounter);
+ topoEntry->set_rx_on_when_idle(neighborInfo.mRxOnWhenIdle);
+ topoEntry->set_secure_data_request(true);
+ topoEntry->set_full_function(neighborInfo.mFullThreadDevice);
+ topoEntry->set_full_network_data(neighborInfo.mFullNetworkData);
+ topoEntry->set_mac_frame_error_rate(static_cast<float>(neighborInfo.mFrameErrorRate) / 0xffff);
+ topoEntry->set_ip_message_error_rate(static_cast<float>(neighborInfo.mMessageErrorRate) / 0xffff);
+ topoEntry->set_version(neighborInfo.mVersion);
+
+ if (!neighborInfo.mIsChild)
+ {
+ continue;
+ }
+
+ auto it = childMap.find(neighborInfo.mRloc16);
+ if (it == childMap.end())
+ {
+ otbrLogErr("Neighbor 0x%04x not found in child table", static_cast<int>(neighborInfo.mRloc16));
+ continue;
+ }
+ const otChildInfo *childInfo = it->second;
+ topoEntry->set_is_child(true);
+ topoEntry->mutable_timeout()->set_seconds(childInfo->mTimeout);
+ topoEntry->set_network_data_version(childInfo->mNetworkDataVersion);
+ }
+ // End of TopoEntry section.
+ }
+
+ {
+ // Begin of WpanBorderRouter section.
+ auto wpanBorderRouter = telemetryData.mutable_wpan_border_router();
+ // Begin of BorderRoutingCounters section.
+ auto borderRoutingCouters = wpanBorderRouter->mutable_border_routing_counters();
+ const otBorderRoutingCounters *otBorderRoutingCounters = otIp6GetBorderRoutingCounters(mInstance);
+
+ borderRoutingCouters->mutable_inbound_unicast()->set_packet_count(
+ otBorderRoutingCounters->mInboundUnicast.mPackets);
+ borderRoutingCouters->mutable_inbound_unicast()->set_byte_count(
+ otBorderRoutingCounters->mInboundUnicast.mBytes);
+ borderRoutingCouters->mutable_inbound_multicast()->set_packet_count(
+ otBorderRoutingCounters->mInboundMulticast.mPackets);
+ borderRoutingCouters->mutable_inbound_multicast()->set_byte_count(
+ otBorderRoutingCounters->mInboundMulticast.mBytes);
+ borderRoutingCouters->mutable_outbound_unicast()->set_packet_count(
+ otBorderRoutingCounters->mOutboundUnicast.mPackets);
+ borderRoutingCouters->mutable_outbound_unicast()->set_byte_count(
+ otBorderRoutingCounters->mOutboundUnicast.mBytes);
+ borderRoutingCouters->mutable_outbound_multicast()->set_packet_count(
+ otBorderRoutingCounters->mOutboundMulticast.mPackets);
+ borderRoutingCouters->mutable_outbound_multicast()->set_byte_count(
+ otBorderRoutingCounters->mOutboundMulticast.mBytes);
+ borderRoutingCouters->set_ra_rx(otBorderRoutingCounters->mRaRx);
+ borderRoutingCouters->set_ra_tx_success(otBorderRoutingCounters->mRaTxSuccess);
+ borderRoutingCouters->set_ra_tx_failure(otBorderRoutingCounters->mRaTxFailure);
+ borderRoutingCouters->set_rs_rx(otBorderRoutingCounters->mRsRx);
+ borderRoutingCouters->set_rs_tx_success(otBorderRoutingCounters->mRsTxSuccess);
+ borderRoutingCouters->set_rs_tx_failure(otBorderRoutingCounters->mRsTxFailure);
+
+#if OTBR_ENABLE_NAT64
+ {
+ auto nat64IcmpCounters = borderRoutingCouters->mutable_nat64_protocol_counters()->mutable_icmp();
+ auto nat64UdpCounters = borderRoutingCouters->mutable_nat64_protocol_counters()->mutable_udp();
+ auto nat64TcpCounters = borderRoutingCouters->mutable_nat64_protocol_counters()->mutable_tcp();
+ otNat64ProtocolCounters otCounters;
+
+ otNat64GetCounters(mInstance, &otCounters);
+ nat64IcmpCounters->set_ipv4_to_ipv6_packets(otCounters.mIcmp.m4To6Packets);
+ nat64IcmpCounters->set_ipv4_to_ipv6_bytes(otCounters.mIcmp.m4To6Bytes);
+ nat64IcmpCounters->set_ipv6_to_ipv4_packets(otCounters.mIcmp.m6To4Packets);
+ nat64IcmpCounters->set_ipv6_to_ipv4_bytes(otCounters.mIcmp.m6To4Bytes);
+ nat64UdpCounters->set_ipv4_to_ipv6_packets(otCounters.mUdp.m4To6Packets);
+ nat64UdpCounters->set_ipv4_to_ipv6_bytes(otCounters.mUdp.m4To6Bytes);
+ nat64UdpCounters->set_ipv6_to_ipv4_packets(otCounters.mUdp.m6To4Packets);
+ nat64UdpCounters->set_ipv6_to_ipv4_bytes(otCounters.mUdp.m6To4Bytes);
+ nat64TcpCounters->set_ipv4_to_ipv6_packets(otCounters.mTcp.m4To6Packets);
+ nat64TcpCounters->set_ipv4_to_ipv6_bytes(otCounters.mTcp.m4To6Bytes);
+ nat64TcpCounters->set_ipv6_to_ipv4_packets(otCounters.mTcp.m6To4Packets);
+ nat64TcpCounters->set_ipv6_to_ipv4_bytes(otCounters.mTcp.m6To4Bytes);
+ }
+
+ {
+ auto errorCounters = borderRoutingCouters->mutable_nat64_error_counters();
+ otNat64ErrorCounters otCounters;
+ otNat64GetErrorCounters(mInstance, &otCounters);
+
+ errorCounters->mutable_unknown()->set_ipv4_to_ipv6_packets(
+ otCounters.mCount4To6[OT_NAT64_DROP_REASON_UNKNOWN]);
+ errorCounters->mutable_unknown()->set_ipv6_to_ipv4_packets(
+ otCounters.mCount6To4[OT_NAT64_DROP_REASON_UNKNOWN]);
+ errorCounters->mutable_illegal_packet()->set_ipv4_to_ipv6_packets(
+ otCounters.mCount4To6[OT_NAT64_DROP_REASON_ILLEGAL_PACKET]);
+ errorCounters->mutable_illegal_packet()->set_ipv6_to_ipv4_packets(
+ otCounters.mCount6To4[OT_NAT64_DROP_REASON_ILLEGAL_PACKET]);
+ errorCounters->mutable_unsupported_protocol()->set_ipv4_to_ipv6_packets(
+ otCounters.mCount4To6[OT_NAT64_DROP_REASON_UNSUPPORTED_PROTO]);
+ errorCounters->mutable_unsupported_protocol()->set_ipv6_to_ipv4_packets(
+ otCounters.mCount6To4[OT_NAT64_DROP_REASON_UNSUPPORTED_PROTO]);
+ errorCounters->mutable_no_mapping()->set_ipv4_to_ipv6_packets(
+ otCounters.mCount4To6[OT_NAT64_DROP_REASON_NO_MAPPING]);
+ errorCounters->mutable_no_mapping()->set_ipv6_to_ipv4_packets(
+ otCounters.mCount6To4[OT_NAT64_DROP_REASON_NO_MAPPING]);
+ }
+#endif // OTBR_ENABLE_NAT64
+ // End of BorderRoutingCounters section.
+
+#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
+ // Begin of SrpServerInfo section.
+ {
+ auto srpServer = wpanBorderRouter->mutable_srp_server();
+ otSrpServerLeaseInfo leaseInfo;
+ const otSrpServerHost *host = nullptr;
+ const otSrpServerResponseCounters *responseCounters = otSrpServerGetResponseCounters(mInstance);
+
+ srpServer->set_state(SrpServerStateFromOtSrpServerState(otSrpServerGetState(mInstance)));
+ srpServer->set_port(otSrpServerGetPort(mInstance));
+ srpServer->set_address_mode(
+ SrpServerAddressModeFromOtSrpServerAddressMode(otSrpServerGetAddressMode(mInstance)));
+
+ auto srpServerHosts = srpServer->mutable_hosts();
+ auto srpServerServices = srpServer->mutable_services();
+ auto srpServerResponseCounters = srpServer->mutable_response_counters();
+
+ while ((host = otSrpServerGetNextHost(mInstance, host)))
+ {
+ const otSrpServerService *service = nullptr;
+
+ if (otSrpServerHostIsDeleted(host))
+ {
+ srpServerHosts->set_deleted_count(srpServerHosts->deleted_count() + 1);
+ }
+ else
+ {
+ srpServerHosts->set_fresh_count(srpServerHosts->fresh_count() + 1);
+ otSrpServerHostGetLeaseInfo(host, &leaseInfo);
+ srpServerHosts->set_lease_time_total_ms(srpServerHosts->lease_time_total_ms() + leaseInfo.mLease);
+ srpServerHosts->set_key_lease_time_total_ms(srpServerHosts->key_lease_time_total_ms() +
+ leaseInfo.mKeyLease);
+ srpServerHosts->set_remaining_lease_time_total_ms(srpServerHosts->remaining_lease_time_total_ms() +
+ leaseInfo.mRemainingLease);
+ srpServerHosts->set_remaining_key_lease_time_total_ms(
+ srpServerHosts->remaining_key_lease_time_total_ms() + leaseInfo.mRemainingKeyLease);
+ }
+
+ while ((service = otSrpServerHostGetNextService(host, service)))
+ {
+ if (otSrpServerServiceIsDeleted(service))
+ {
+ srpServerServices->set_deleted_count(srpServerServices->deleted_count() + 1);
+ }
+ else
+ {
+ srpServerServices->set_fresh_count(srpServerServices->fresh_count() + 1);
+ otSrpServerServiceGetLeaseInfo(service, &leaseInfo);
+ srpServerServices->set_lease_time_total_ms(srpServerServices->lease_time_total_ms() +
+ leaseInfo.mLease);
+ srpServerServices->set_key_lease_time_total_ms(srpServerServices->key_lease_time_total_ms() +
+ leaseInfo.mKeyLease);
+ srpServerServices->set_remaining_lease_time_total_ms(
+ srpServerServices->remaining_lease_time_total_ms() + leaseInfo.mRemainingLease);
+ srpServerServices->set_remaining_key_lease_time_total_ms(
+ srpServerServices->remaining_key_lease_time_total_ms() + leaseInfo.mRemainingKeyLease);
+ }
+ }
+ }
+
+ srpServerResponseCounters->set_success_count(responseCounters->mSuccess);
+ srpServerResponseCounters->set_server_failure_count(responseCounters->mServerFailure);
+ srpServerResponseCounters->set_format_error_count(responseCounters->mFormatError);
+ srpServerResponseCounters->set_name_exists_count(responseCounters->mNameExists);
+ srpServerResponseCounters->set_refused_count(responseCounters->mRefused);
+ srpServerResponseCounters->set_other_count(responseCounters->mOther);
+ }
+ // End of SrpServerInfo section.
+#endif // OTBR_ENABLE_SRP_ADVERTISING_PROXY
+
+#if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
+ // Begin of DnsServerInfo section.
+ {
+ auto dnsServer = wpanBorderRouter->mutable_dns_server();
+ auto dnsServerResponseCounters = dnsServer->mutable_response_counters();
+ otDnssdCounters otDnssdCounters = *otDnssdGetCounters(mInstance);
+
+ dnsServerResponseCounters->set_success_count(otDnssdCounters.mSuccessResponse);
+ dnsServerResponseCounters->set_server_failure_count(otDnssdCounters.mServerFailureResponse);
+ dnsServerResponseCounters->set_format_error_count(otDnssdCounters.mFormatErrorResponse);
+ dnsServerResponseCounters->set_name_error_count(otDnssdCounters.mNameErrorResponse);
+ dnsServerResponseCounters->set_not_implemented_count(otDnssdCounters.mNotImplementedResponse);
+ dnsServerResponseCounters->set_other_count(otDnssdCounters.mOtherResponse);
+
+ dnsServer->set_resolved_by_local_srp_count(otDnssdCounters.mResolvedBySrp);
+ }
+ // End of DnsServerInfo section.
+#endif // OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
+
+ // Start of MdnsInfo section.
+ if (aPublisher != nullptr)
+ {
+ auto mdns = wpanBorderRouter->mutable_mdns();
+ const MdnsTelemetryInfo &mdnsInfo = aPublisher->GetMdnsTelemetryInfo();
+
+ CopyMdnsResponseCounters(mdnsInfo.mHostRegistrations, mdns->mutable_host_registration_responses());
+ CopyMdnsResponseCounters(mdnsInfo.mServiceRegistrations, mdns->mutable_service_registration_responses());
+ CopyMdnsResponseCounters(mdnsInfo.mHostResolutions, mdns->mutable_host_resolution_responses());
+ CopyMdnsResponseCounters(mdnsInfo.mServiceResolutions, mdns->mutable_service_resolution_responses());
+
+ mdns->set_host_registration_ema_latency_ms(mdnsInfo.mHostRegistrationEmaLatency);
+ mdns->set_service_registration_ema_latency_ms(mdnsInfo.mServiceRegistrationEmaLatency);
+ mdns->set_host_resolution_ema_latency_ms(mdnsInfo.mHostResolutionEmaLatency);
+ mdns->set_service_resolution_ema_latency_ms(mdnsInfo.mServiceResolutionEmaLatency);
+ }
+ // End of MdnsInfo section.
+
+#if OTBR_ENABLE_NAT64
+ // Start of BorderRoutingNat64State section.
+ {
+ auto nat64State = wpanBorderRouter->mutable_nat64_state();
+
+ nat64State->set_prefix_manager_state(Nat64StateFromOtNat64State(otNat64GetPrefixManagerState(mInstance)));
+ nat64State->set_translator_state(Nat64StateFromOtNat64State(otNat64GetTranslatorState(mInstance)));
+ }
+ // End of BorderRoutingNat64State section.
+
+ // Start of Nat64Mapping section.
+ {
+ otNat64AddressMappingIterator iterator;
+ otNat64AddressMapping otMapping;
+ Sha256::Hash hash;
+ Sha256 sha256;
+
+ otNat64InitAddressMappingIterator(mInstance, &iterator);
+ while (otNat64GetNextAddressMapping(mInstance, &iterator, &otMapping) == OT_ERROR_NONE)
+ {
+ auto nat64Mapping = wpanBorderRouter->add_nat64_mappings();
+ auto nat64MappingCounters = nat64Mapping->mutable_counters();
+
+ nat64Mapping->set_mapping_id(otMapping.mId);
+ CopyNat64TrafficCounters(otMapping.mCounters.mTcp, nat64MappingCounters->mutable_tcp());
+ CopyNat64TrafficCounters(otMapping.mCounters.mUdp, nat64MappingCounters->mutable_udp());
+ CopyNat64TrafficCounters(otMapping.mCounters.mIcmp, nat64MappingCounters->mutable_icmp());
+
+ {
+ uint8_t ipAddrShaInput[OT_IP6_ADDRESS_SIZE + kNat64SourceAddressHashSaltLength];
+ memcpy(ipAddrShaInput, otMapping.mIp6.mFields.m8, sizeof(otMapping.mIp6.mFields.m8));
+ memcpy(&ipAddrShaInput[sizeof(otMapping.mIp6.mFields.m8)], mNat64Ipv6AddressSalt,
+ sizeof(mNat64Ipv6AddressSalt));
+
+ sha256.Start();
+ sha256.Update(ipAddrShaInput, sizeof(ipAddrShaInput));
+ sha256.Finish(hash);
+
+ nat64Mapping->mutable_hashed_ipv6_address()->append(reinterpret_cast<const char *>(hash.GetBytes()),
+ sizeof(hash.GetBytes()));
+ // Remaining time is not included in the telemetry
+ }
+ }
+ }
+ // End of Nat64Mapping section.
+#endif // OTBR_ENABLE_NAT64
+
+ // End of WpanBorderRouter section.
+
+ // Start of WpanRcp section.
+ {
+ auto wpanRcp = telemetryData.mutable_wpan_rcp();
+ auto rcpStabilityStatistics = wpanRcp->mutable_rcp_stability_statistics();
+ otRadioSpinelMetrics otRadioSpinelMetrics = *otSysGetRadioSpinelMetrics();
+
+ rcpStabilityStatistics->set_rcp_timeout_count(otRadioSpinelMetrics.mRcpTimeoutCount);
+ rcpStabilityStatistics->set_rcp_reset_count(otRadioSpinelMetrics.mRcpUnexpectedResetCount);
+ rcpStabilityStatistics->set_rcp_restoration_count(otRadioSpinelMetrics.mRcpRestorationCount);
+ rcpStabilityStatistics->set_spinel_parse_error_count(otRadioSpinelMetrics.mSpinelParseErrorCount);
+
+ // TODO: provide rcp_firmware_update_count info.
+ rcpStabilityStatistics->set_thread_stack_uptime(otInstanceGetUptime(mInstance));
+
+ auto rcpInterfaceStatistics = wpanRcp->mutable_rcp_interface_statistics();
+ otRcpInterfaceMetrics otRcpInterfaceMetrics = *otSysGetRcpInterfaceMetrics();
+
+ rcpInterfaceStatistics->set_rcp_interface_type(otRcpInterfaceMetrics.mRcpInterfaceType);
+ rcpInterfaceStatistics->set_transferred_frames_count(otRcpInterfaceMetrics.mTransferredFrameCount);
+ rcpInterfaceStatistics->set_transferred_valid_frames_count(
+ otRcpInterfaceMetrics.mTransferredValidFrameCount);
+ rcpInterfaceStatistics->set_transferred_garbage_frames_count(
+ otRcpInterfaceMetrics.mTransferredGarbageFrameCount);
+ rcpInterfaceStatistics->set_rx_frames_count(otRcpInterfaceMetrics.mRxFrameCount);
+ rcpInterfaceStatistics->set_rx_bytes_count(otRcpInterfaceMetrics.mRxFrameByteCount);
+ rcpInterfaceStatistics->set_tx_frames_count(otRcpInterfaceMetrics.mTxFrameCount);
+ rcpInterfaceStatistics->set_tx_bytes_count(otRcpInterfaceMetrics.mTxFrameByteCount);
+ }
+ // End of WpanRcp section.
+
+ // Start of CoexMetrics section.
+ {
+ auto coexMetrics = telemetryData.mutable_coex_metrics();
+ otRadioCoexMetrics otRadioCoexMetrics;
+
+ SuccessOrExit(error = otPlatRadioGetCoexMetrics(mInstance, &otRadioCoexMetrics));
+ coexMetrics->set_count_tx_request(otRadioCoexMetrics.mNumTxRequest);
+ coexMetrics->set_count_tx_grant_immediate(otRadioCoexMetrics.mNumTxGrantImmediate);
+ coexMetrics->set_count_tx_grant_wait(otRadioCoexMetrics.mNumTxGrantWait);
+ coexMetrics->set_count_tx_grant_wait_activated(otRadioCoexMetrics.mNumTxGrantWaitActivated);
+ coexMetrics->set_count_tx_grant_wait_timeout(otRadioCoexMetrics.mNumTxGrantWaitTimeout);
+ coexMetrics->set_count_tx_grant_deactivated_during_request(
+ otRadioCoexMetrics.mNumTxGrantDeactivatedDuringRequest);
+ coexMetrics->set_tx_average_request_to_grant_time_us(otRadioCoexMetrics.mAvgTxRequestToGrantTime);
+ coexMetrics->set_count_rx_request(otRadioCoexMetrics.mNumRxRequest);
+ coexMetrics->set_count_rx_grant_immediate(otRadioCoexMetrics.mNumRxGrantImmediate);
+ coexMetrics->set_count_rx_grant_wait(otRadioCoexMetrics.mNumRxGrantWait);
+ coexMetrics->set_count_rx_grant_wait_activated(otRadioCoexMetrics.mNumRxGrantWaitActivated);
+ coexMetrics->set_count_rx_grant_wait_timeout(otRadioCoexMetrics.mNumRxGrantWaitTimeout);
+ coexMetrics->set_count_rx_grant_deactivated_during_request(
+ otRadioCoexMetrics.mNumRxGrantDeactivatedDuringRequest);
+ coexMetrics->set_count_rx_grant_none(otRadioCoexMetrics.mNumRxGrantNone);
+ coexMetrics->set_rx_average_request_to_grant_time_us(otRadioCoexMetrics.mAvgRxRequestToGrantTime);
+ }
+ // End of CoexMetrics section.
+ }
+
+exit:
+ return error;
+}
+#endif // OTBR_ENABLE_TELEMETRY_DATA_API
} // namespace agent
} // namespace otbr
diff --git a/src/utils/thread_helper.hpp b/src/utils/thread_helper.hpp
index af8ef1a9..10d38770 100644
--- a/src/utils/thread_helper.hpp
+++ b/src/utils/thread_helper.hpp
@@ -34,6 +34,8 @@
#ifndef OTBR_THREAD_HELPER_HPP_
#define OTBR_THREAD_HELPER_HPP_
+#include "openthread-br/config.h"
+
#include <chrono>
#include <functional>
#include <map>
@@ -47,6 +49,10 @@
#include <openthread/joiner.h>
#include <openthread/netdata.h>
#include <openthread/thread.h>
+#include "mdns/mdns.hpp"
+#if OTBR_ENABLE_TELEMETRY_DATA_API
+#include "proto/thread_telemetry.pb.h"
+#endif
namespace otbr {
namespace Ncp {
@@ -250,6 +256,18 @@ public:
void DetachGracefully(ResultHandler aHandler);
+#if OTBR_ENABLE_TELEMETRY_DATA_API
+ /**
+ * This method populates the telemetry data and returns the error code if error happens.
+ *
+ * @param[in] aPublisher The Mdns::Publisher to provide MDNS telemetry if it is not `nullptr`.
+ * @param[in] telemetryData The telemetry data to be populated.
+ *
+ * @returns The error code if error happens during the population of the telemetry data.
+ */
+ otError RetrieveTelemetryData(Mdns::Publisher *aPublisher, threadnetwork::TelemetryData &telemetryData);
+#endif // OTBR_ENABLE_TELEMETRY_DATA_API
+
/**
* This method logs OpenThread action result.
*
@@ -309,6 +327,11 @@ private:
#if OTBR_ENABLE_DBUS_SERVER
UpdateMeshCopTxtHandler mUpdateMeshCopTxtHandler;
#endif
+
+#if OTBR_ENABLE_TELEMETRY_DATA_API & OTBR_ENABLE_NAT64
+ static const uint8_t kNat64SourceAddressHashSaltLength = 16;
+ uint8_t mNat64Ipv6AddressSalt[kNat64SourceAddressHashSaltLength];
+#endif
};
} // namespace agent
diff --git a/tests/dbus/CMakeLists.txt b/tests/dbus/CMakeLists.txt
index 12f81e1b..5aa3f471 100644
--- a/tests/dbus/CMakeLists.txt
+++ b/tests/dbus/CMakeLists.txt
@@ -29,8 +29,10 @@
add_executable(otbr-test-dbus-client
test_dbus_client.cpp
)
+
target_link_libraries(otbr-test-dbus-client PRIVATE
otbr-dbus-client
+ otbr-proto
)
add_executable(otbr-test-dbus-server
diff --git a/tests/dbus/test-client b/tests/dbus/test-client
index 05a8c42c..558a7c56 100755
--- a/tests/dbus/test-client
+++ b/tests/dbus/test-client
@@ -221,11 +221,15 @@ main()
# The ot-cli-ftd node is used to test Thread attach.
expect <<EOF &
spawn ot-cli-ftd 3
-send "panid 0x7890\r\n"
+send "dataset init new\r\n"
expect "Done"
-send "networkname Test1\r\n"
+send "dataset panid 0x7890\r\n"
expect "Done"
-send "channel 15\r\n"
+send "dataset networkname Test1\r\n"
+expect "Done"
+send "dataset channel 15\r\n"
+expect "Done"
+send "dataset commit active\r\n"
expect "Done"
send "ifconfig up\r\n"
expect "Done"
@@ -249,13 +253,17 @@ EOF
# The ot-cli-mtd node is used to test the child and neighbor table.
expect <<EOF &
spawn ot-cli-mtd 2
-send "panid 0x3456\r\n"
+send "dataset clear"
+expect "Done"
+send "dataset panid 0x3456\r\n"
+expect "Done"
+send "dataset networkkey 00112233445566778899aabbccddeeff\r\n"
expect "Done"
-send "networkkey 00112233445566778899aabbccddeeff\r\n"
+send "dataset networkname Test\r\n"
expect "Done"
-send "networkname Test\r\n"
+send "dataset channel 11\r\n"
expect "Done"
-send "channel 11\r\n"
+send "dataset commit active\r\n"
expect "Done"
send "ifconfig up\r\n"
expect "Done"
diff --git a/tests/dbus/test_dbus_client.cpp b/tests/dbus/test_dbus_client.cpp
index ab3aa8f8..0e0e6134 100644
--- a/tests/dbus/test_dbus_client.cpp
+++ b/tests/dbus/test_dbus_client.cpp
@@ -39,6 +39,10 @@
#include "common/code_utils.hpp"
#include "dbus/client/thread_api_dbus.hpp"
#include "dbus/common/constants.hpp"
+#if OTBR_ENABLE_TELEMETRY_DATA_API
+#include "proto/thread_telemetry.pb.h"
+#endif
+#include "proto/capabilities.pb.h"
using otbr::DBus::ActiveScanResult;
using otbr::DBus::ClientError;
@@ -239,6 +243,73 @@ void CheckNat64(ThreadApiDBus *aApi)
#endif
}
+#if OTBR_ENABLE_TELEMETRY_DATA_API
+void CheckTelemetryData(ThreadApiDBus *aApi)
+{
+ std::vector<uint8_t> responseTelemetryDataBytes;
+ threadnetwork::TelemetryData telemetryData;
+
+ TEST_ASSERT(aApi->GetTelemetryData(responseTelemetryDataBytes) == OTBR_ERROR_NONE);
+ // Print TelemetryData proto in hex format.
+ printf("TelemetryData bytes in hex: ");
+ for (uint8_t byte : responseTelemetryDataBytes)
+ {
+ printf("%02x ", byte);
+ }
+ printf("\n");
+
+ TEST_ASSERT(telemetryData.ParseFromString(
+ std::string(responseTelemetryDataBytes.begin(), responseTelemetryDataBytes.end())));
+ TEST_ASSERT(telemetryData.wpan_stats().node_type() == threadnetwork::TelemetryData::NODE_TYPE_LEADER);
+ TEST_ASSERT(telemetryData.wpan_stats().channel() == 11);
+ TEST_ASSERT(telemetryData.wpan_stats().radio_tx_power() == 0);
+ TEST_ASSERT(telemetryData.wpan_stats().mac_cca_fail_rate() < 1e-6);
+ TEST_ASSERT(telemetryData.wpan_stats().phy_tx() > 0);
+ TEST_ASSERT(telemetryData.wpan_stats().phy_rx() > 0);
+ TEST_ASSERT(telemetryData.wpan_stats().ip_tx_success() > 0);
+ TEST_ASSERT(telemetryData.wpan_topo_full().rloc16() > 0);
+ TEST_ASSERT(telemetryData.wpan_topo_full().network_data().size() > 0);
+ TEST_ASSERT(telemetryData.wpan_topo_full().partition_id() > 0);
+ TEST_ASSERT(telemetryData.wpan_topo_full().extended_pan_id() > 0);
+ TEST_ASSERT(telemetryData.topo_entries_size() == 1);
+ TEST_ASSERT(telemetryData.topo_entries(0).rloc16() > 0);
+ TEST_ASSERT(telemetryData.wpan_border_router().border_routing_counters().rs_tx_failure() == 0);
+#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
+ TEST_ASSERT(telemetryData.wpan_border_router().srp_server().state() ==
+ threadnetwork::TelemetryData::SRP_SERVER_STATE_RUNNING);
+#endif
+#if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
+ TEST_ASSERT(telemetryData.wpan_border_router().dns_server().response_counters().server_failure_count() == 0);
+#endif
+ TEST_ASSERT(telemetryData.wpan_border_router().mdns().service_registration_responses().success_count() > 0);
+#if OTBR_ENABLE_NAT64
+ TEST_ASSERT(telemetryData.wpan_border_router().nat64_state().prefix_manager_state() ==
+ threadnetwork::TelemetryData::NAT64_STATE_NOT_RUNNING);
+#endif
+ TEST_ASSERT(telemetryData.wpan_rcp().rcp_interface_statistics().transferred_frames_count() > 0);
+ TEST_ASSERT(telemetryData.coex_metrics().count_tx_request() > 0);
+}
+#endif
+
+void CheckCapabilities(ThreadApiDBus *aApi)
+{
+ std::vector<uint8_t> responseCapabilitiesBytes;
+ otbr::Capabilities capabilities;
+
+ TEST_ASSERT(aApi->GetCapabilities(responseCapabilitiesBytes) == OTBR_ERROR_NONE);
+ // Print TelemetryData proto in hex format.
+ printf("TelemetryData bytes in hex: ");
+ for (uint8_t byte : responseCapabilitiesBytes)
+ {
+ printf("%02x ", byte);
+ }
+ printf("\n");
+
+ TEST_ASSERT(
+ capabilities.ParseFromString(std::string(responseCapabilitiesBytes.begin(), responseCapabilitiesBytes.end())));
+ TEST_ASSERT(capabilities.nat64() == OTBR_ENABLE_NAT64);
+}
+
int main()
{
DBusError error;
@@ -267,7 +338,6 @@ int main()
TEST_ASSERT(region == "US");
TEST_ASSERT(api->GetPreferredChannelMask(preferredChannelMask) == ClientError::ERROR_NONE);
- TEST_ASSERT(preferredChannelMask == 0x7fff800);
api->EnergyScan(scanDuration, [&stepDone](const std::vector<EnergyScanResult> &aResult) {
TEST_ASSERT(!aResult.empty());
@@ -350,6 +420,10 @@ int main()
CheckMdnsInfo(api.get());
CheckDnssdCounters(api.get());
CheckNat64(api.get());
+#if OTBR_ENABLE_TELEMETRY_DATA_API
+ CheckTelemetryData(api.get());
+#endif
+ CheckCapabilities(api.get());
api->FactoryReset(nullptr);
TEST_ASSERT(api->GetNetworkName(name) == OTBR_ERROR_NONE);
TEST_ASSERT(rloc16 != 0xffff);
diff --git a/tests/mdns/main.cpp b/tests/mdns/main.cpp
index 758ed1b2..66f6366c 100644
--- a/tests/mdns/main.cpp
+++ b/tests/mdns/main.cpp
@@ -96,14 +96,17 @@ void PublishSingleServiceWithCustomHost(void *aContext, Mdns::Publisher::State a
VerifyOrDie(aContext == &sContext, "unexpected context");
if (aState == Mdns::Publisher::State::kReady)
{
+ Mdns::Publisher::TxtData txtData;
Mdns::Publisher::TxtList txtList{
{"nn", "cool"}, {"xp", xpanid, sizeof(xpanid)}, {"tv", "1.1.1"}, {"xa", extAddr, sizeof(extAddr)}};
+ Mdns::Publisher::EncodeTxtData(txtList, txtData);
+
sContext.mPublisher->PublishHost(hostName, {Ip6Address(hostAddr)},
[](otbrError aError) { SuccessOrDie(aError, "cannot publish the host"); });
sContext.mPublisher->PublishService(
- hostName, "SingleService", "_meshcop._udp.", Mdns::Publisher::SubTypeList{}, 12345, txtList,
+ hostName, "SingleService", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData,
[](otbrError aError) { SuccessOrDie(aError, "cannot publish the service"); });
}
}
@@ -123,18 +126,21 @@ void PublishMultipleServicesWithCustomHost(void *aContext, Mdns::Publisher::Stat
VerifyOrDie(aContext == &sContext, "unexpected context");
if (aState == Mdns::Publisher::State::kReady)
{
+ Mdns::Publisher::TxtData txtData;
Mdns::Publisher::TxtList txtList{
{"nn", "cool"}, {"xp", xpanid, sizeof(xpanid)}, {"tv", "1.1.1"}, {"xa", extAddr, sizeof(extAddr)}};
+ Mdns::Publisher::EncodeTxtData(txtList, txtData);
+
sContext.mPublisher->PublishHost(hostName1, {Ip6Address(hostAddr)},
[](otbrError aError) { SuccessOrDie(aError, "cannot publish the host"); });
sContext.mPublisher->PublishService(
- hostName1, "MultipleService11", "_meshcop._udp.", Mdns::Publisher::SubTypeList{}, 12345, txtList,
+ hostName1, "MultipleService11", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData,
[](otbrError aError) { SuccessOrDie(aError, "cannot publish the first service"); });
sContext.mPublisher->PublishService(
- hostName1, "MultipleService12", "_meshcop._udp.", Mdns::Publisher::SubTypeList{}, 12345, txtList,
+ hostName1, "MultipleService12", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData,
[](otbrError aError) { SuccessOrDie(aError, "cannot publish the second service"); });
sContext.mPublisher->PublishHost(hostName2, {Ip6Address(hostAddr)}, [](otbrError aError) {
@@ -142,11 +148,11 @@ void PublishMultipleServicesWithCustomHost(void *aContext, Mdns::Publisher::Stat
});
sContext.mPublisher->PublishService(
- hostName2, "MultipleService21", "_meshcop._udp.", Mdns::Publisher::SubTypeList{}, 12345, txtList,
+ hostName2, "MultipleService21", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData,
[](otbrError aError) { SuccessOrDie(aError, "cannot publish the first service"); });
sContext.mPublisher->PublishService(
- hostName2, "MultipleService22", "_meshcop._udp.", Mdns::Publisher::SubTypeList{}, 12345, txtList,
+ hostName2, "MultipleService22", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData,
[](otbrError aError) { SuccessOrDie(aError, "cannot publish the second service"); });
}
}
@@ -157,15 +163,18 @@ void PublishSingleService(void *aContext, Mdns::Publisher::State aState)
uint8_t xpanid[kSizeExtPanId] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48};
uint8_t extAddr[kSizeExtAddr] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48};
+ Mdns::Publisher::TxtData txtData;
Mdns::Publisher::TxtList txtList{
{"nn", "cool"}, {"xp", xpanid, sizeof(xpanid)}, {"tv", "1.1.1"}, {"xa", extAddr, sizeof(extAddr)}};
+ Mdns::Publisher::EncodeTxtData(txtList, txtData);
+
assert(aContext == &sContext);
if (aState == Mdns::Publisher::State::kReady)
{
sContext.mPublisher->PublishService(
- "", "SingleService", "_meshcop._udp.", Mdns::Publisher::SubTypeList{}, 12345, txtList,
- [](otbrError aError) { SuccessOrDie(aError, "SingleService._meshcop._udp."); });
+ "", "SingleService", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData,
+ [](otbrError aError) { SuccessOrDie(aError, "SingleService._meshcop._udp"); });
}
}
@@ -175,14 +184,17 @@ void PublishSingleServiceWithEmptyName(void *aContext, Mdns::Publisher::State aS
uint8_t xpanid[kSizeExtPanId] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48};
uint8_t extAddr[kSizeExtAddr] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48};
+ Mdns::Publisher::TxtData txtData;
Mdns::Publisher::TxtList txtList{
{"nn", "cool"}, {"xp", xpanid, sizeof(xpanid)}, {"tv", "1.1.1"}, {"xa", extAddr, sizeof(extAddr)}};
+ Mdns::Publisher::EncodeTxtData(txtList, txtData);
+
assert(aContext == &sContext);
if (aState == Mdns::Publisher::State::kReady)
{
- sContext.mPublisher->PublishService("", "", "_meshcop._udp.", Mdns::Publisher::SubTypeList{}, 12345, txtList,
- [](otbrError aError) { SuccessOrDie(aError, "(empty)._meshcop._udp."); });
+ sContext.mPublisher->PublishService("", "", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData,
+ [](otbrError aError) { SuccessOrDie(aError, "(empty)._meshcop._udp"); });
}
}
@@ -196,22 +208,28 @@ void PublishMultipleServices(void *aContext, Mdns::Publisher::State aState)
assert(aContext == &sContext);
if (aState == Mdns::Publisher::State::kReady)
{
+ Mdns::Publisher::TxtData txtData;
Mdns::Publisher::TxtList txtList{
{"nn", "cool1"}, {"xp", xpanid, sizeof(xpanid)}, {"tv", "1.1.1"}, {"xa", extAddr, sizeof(extAddr)}};
+ Mdns::Publisher::EncodeTxtData(txtList, txtData);
+
sContext.mPublisher->PublishService(
- "", "MultipleService1", "_meshcop._udp.", Mdns::Publisher::SubTypeList{}, 12345, txtList,
- [](otbrError aError) { SuccessOrDie(aError, "MultipleService1._meshcop._udp."); });
+ "", "MultipleService1", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData,
+ [](otbrError aError) { SuccessOrDie(aError, "MultipleService1._meshcop._udp"); });
}
if (aState == Mdns::Publisher::State::kReady)
{
+ Mdns::Publisher::TxtData txtData;
Mdns::Publisher::TxtList txtList{
{"nn", "cool2"}, {"xp", xpanid, sizeof(xpanid)}, {"tv", "1.1.1"}, {"xa", extAddr, sizeof(extAddr)}};
+ Mdns::Publisher::EncodeTxtData(txtList, txtData);
+
sContext.mPublisher->PublishService(
- "", "MultipleService2", "_meshcop._udp.", Mdns::Publisher::SubTypeList{}, 12345, txtList,
- [](otbrError aError) { SuccessOrDie(aError, "MultipleService2._meshcop._udp."); });
+ "", "MultipleService2", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData,
+ [](otbrError aError) { SuccessOrDie(aError, "MultipleService2._meshcop._udp"); });
}
}
@@ -226,28 +244,38 @@ void PublishUpdateServices(void *aContext)
assert(aContext == &sContext);
if (!sContext.mUpdate)
{
+ Mdns::Publisher::TxtData txtData;
Mdns::Publisher::TxtList txtList{
{"nn", "cool"}, {"xp", xpanidOld, sizeof(xpanidOld)}, {"tv", "1.1.1"}, {"xa", extAddr, sizeof(extAddr)}};
+ Mdns::Publisher::EncodeTxtData(txtList, txtData);
+
sContext.mPublisher->PublishService(
- "", "UpdateService", "_meshcop._udp.", Mdns::Publisher::SubTypeList{}, 12345, txtList,
+ "", "UpdateService", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData,
[](otbrError aError) { otbrLogResult(aError, "UpdateService._meshcop._udp"); });
}
else
{
+ Mdns::Publisher::TxtData txtData;
Mdns::Publisher::TxtList txtList{{"nn", "coolcool"},
{"xp", xpanidNew, sizeof(xpanidNew)},
{"tv", "1.1.1"},
{"xa", extAddr, sizeof(extAddr)}};
+ Mdns::Publisher::EncodeTxtData(txtList, txtData);
+
sContext.mPublisher->PublishService(
- "", "UpdateService", "_meshcop._udp.", Mdns::Publisher::SubTypeList{}, 12345, txtList,
+ "", "UpdateService", "_meshcop._udp", Mdns::Publisher::SubTypeList{}, 12345, txtData,
[](otbrError aError) { SuccessOrDie(aError, "UpdateService._meshcop._udp"); });
}
}
void PublishServiceSubTypes(void *aContext)
{
+ Mdns::Publisher::TxtData txtData;
+
+ txtData.push_back(0);
+
OT_UNUSED_VARIABLE(aContext);
assert(aContext == &sContext);
@@ -257,8 +285,8 @@ void PublishServiceSubTypes(void *aContext)
subTypeList.back() = "_SUBTYPE3";
sContext.mPublisher->PublishService(
- "", "ServiceWithSubTypes", "_meshcop._udp.", subTypeList, 12345, Mdns::Publisher::TxtList{},
- [](otbrError aError) { SuccessOrDie(aError, "ServiceWithSubTypes._meshcop._udp."); });
+ "", "ServiceWithSubTypes", "_meshcop._udp", subTypeList, 12345, txtData,
+ [](otbrError aError) { SuccessOrDie(aError, "ServiceWithSubTypes._meshcop._udp"); });
}
otbrError TestSingleServiceWithCustomHost(void)
@@ -410,10 +438,65 @@ exit:
return ret;
}
+otbrError CheckTxtDataEncoderDecoder(void)
+{
+ otbrError error = OTBR_ERROR_NONE;
+ Mdns::Publisher::TxtList txtList;
+ Mdns::Publisher::TxtList parsedTxtList;
+ std::vector<uint8_t> txtData;
+
+ // Encode empty `TxtList`
+
+ SuccessOrExit(error = Mdns::Publisher::EncodeTxtData(txtList, txtData));
+ VerifyOrExit(txtData.size() == 1, error = OTBR_ERROR_PARSE);
+ VerifyOrExit(txtData[0] == 0, error = OTBR_ERROR_PARSE);
+
+ SuccessOrExit(error = Mdns::Publisher::DecodeTxtData(parsedTxtList, txtData.data(), txtData.size()));
+ VerifyOrExit(parsedTxtList.size() == 0, error = OTBR_ERROR_PARSE);
+
+ // TxtList with one bool attribute
+
+ txtList.clear();
+ txtList.emplace_back("b1");
+
+ SuccessOrExit(error = Mdns::Publisher::EncodeTxtData(txtList, txtData));
+ SuccessOrExit(error = Mdns::Publisher::DecodeTxtData(parsedTxtList, txtData.data(), txtData.size()));
+ VerifyOrExit(parsedTxtList == txtList, error = OTBR_ERROR_PARSE);
+
+ // TxtList with one one key/value
+
+ txtList.clear();
+ txtList.emplace_back("k1", "v1");
+
+ SuccessOrExit(error = Mdns::Publisher::EncodeTxtData(txtList, txtData));
+ SuccessOrExit(error = Mdns::Publisher::DecodeTxtData(parsedTxtList, txtData.data(), txtData.size()));
+ VerifyOrExit(parsedTxtList == txtList, error = OTBR_ERROR_PARSE);
+
+ // TxtList with multiple entries
+
+ txtList.clear();
+ txtList.emplace_back("k1", "v1");
+ txtList.emplace_back("b1");
+ txtList.emplace_back("b2");
+ txtList.emplace_back("k2", "valu2");
+
+ SuccessOrExit(error = Mdns::Publisher::EncodeTxtData(txtList, txtData));
+ SuccessOrExit(error = Mdns::Publisher::DecodeTxtData(parsedTxtList, txtData.data(), txtData.size()));
+ VerifyOrExit(parsedTxtList == txtList, error = OTBR_ERROR_PARSE);
+
+exit:
+ return error;
+}
+
int main(int argc, char *argv[])
{
int ret = 0;
+ if (CheckTxtDataEncoderDecoder() != OTBR_ERROR_NONE)
+ {
+ return 1;
+ }
+
if (argc < 2)
{
return 1;
diff --git a/tests/rest/test-rest-server b/tests/rest/test-rest-server
index 3914bd88..cc63de06 100755
--- a/tests/rest/test-rest-server
+++ b/tests/rest/test-rest-server
@@ -50,6 +50,10 @@ main()
sleep 1
sudo expect <<EOF &
spawn ${CMAKE_BINARY_DIR}/third_party/openthread/repo/src/posix/ot-ctl
+send "dataset init new\r\n"
+expect "Done"
+send "dataset commit active\r\n"
+expect "Done"
send "ifconfig up\r\n"
expect "Done"
send "thread start\r\n"
diff --git a/tests/rest/test_rest.py b/tests/rest/test_rest.py
index 6a846489..b419c2d6 100644
--- a/tests/rest/test_rest.py
+++ b/tests/rest/test_rest.py
@@ -194,7 +194,7 @@ def node_check(data):
"Rloc16", "LeaderData", "ExtPanId"
]
expected_value_type = [
- int, int, str, str, str, int, dict, str
+ str, int, str, str, str, int, dict, str
]
expected_check_dict = dict(zip(expected_keys, expected_value_type))
@@ -250,7 +250,7 @@ def node_ext_address_check(data):
def node_state_check(data):
assert data is not None
- assert (type(data) == int)
+ assert (type(data) == str)
return True
diff --git a/tests/scripts/bootstrap.sh b/tests/scripts/bootstrap.sh
index a89e9ccd..f3bdf513 100755
--- a/tests/scripts/bootstrap.sh
+++ b/tests/scripts/bootstrap.sh
@@ -32,6 +32,8 @@ set -euxo pipefail
TOOLS_HOME="$HOME"/.cache/tools
[[ -d $TOOLS_HOME ]] || mkdir -p "$TOOLS_HOME"
+MDNSRESPONDER_PATCH_PATH=$(realpath "$(dirname "$0")"/../../third_party/mDNSResponder)
+
disable_install_recommends()
{
OTBR_APT_CONF_FILE=/etc/apt/apt.conf
@@ -126,15 +128,18 @@ case "$(uname)" in
fi
if [ "${OTBR_MDNS-}" == 'mDNSResponder' ]; then
- SOURCE_NAME=mDNSResponder-1310.80.1
+ SOURCE_NAME=mDNSResponder-1790.80.10
wget https://github.com/apple-oss-distributions/mDNSResponder/archive/refs/tags/$SOURCE_NAME.tar.gz \
&& mkdir -p $SOURCE_NAME \
&& tar xvf $SOURCE_NAME.tar.gz -C $SOURCE_NAME --strip-components=1 \
- && cd $SOURCE_NAME/Clients \
- && sed -i '/#include <ctype.h>/a #include <stdarg.h>' dns-sd.c \
- && sed -i '/#include <ctype.h>/a #include <sys/param.h>' dns-sd.c \
- && cd ../mDNSPosix \
- && make os=linux && sudo make install os=linux
+ && cd "$SOURCE_NAME" \
+ && (
+ for patch in "$MDNSRESPONDER_PATCH_PATH"/*.patch; do
+ patch -p1 <"$patch"
+ done
+ ) \
+ && cd mDNSPosix \
+ && make os=linux tls=no && sudo make install os=linux tls=no
fi
# Enable IPv6
diff --git a/tests/scripts/check-scan-build b/tests/scripts/check-scan-build
index 77d2058f..42faf1c4 100755
--- a/tests/scripts/check-scan-build
+++ b/tests/scripts/check-scan-build
@@ -40,6 +40,7 @@ main()
-DCMAKE_C_COMPILER=clang \
-DOTBR_DBUS=ON \
-DOTBR_FEATURE_FLAGS=ON \
+ -DOTBR_TELEMETRY_DATA_API=ON \
-DOTBR_WEB=ON \
-DOTBR_REST=ON \
.. \
diff --git a/tests/scripts/openwrt b/tests/scripts/openwrt
index a960cb99..355e869a 100755
--- a/tests/scripts/openwrt
+++ b/tests/scripts/openwrt
@@ -43,7 +43,7 @@ readonly CONTAINER_NAME
do_prepare()
{
- docker create --name "${CONTAINER_NAME}" --rm -v openwrt-bin:/home/build/openwrt/bin -it openwrt/sdk
+ docker create --name "${CONTAINER_NAME}" --rm -v openwrt-bin:/home/build/openwrt/bin -it openwrt/sdk:22.03.3
docker cp . "${CONTAINER_NAME}":/home/build/ot-br-posix
echo 'src-link openthread /home/build/ot-br-posix/etc/openwrt' >feeds.conf
diff --git a/third_party/mDNSResponder/0001-Fix-Linux-build.patch b/third_party/mDNSResponder/0001-Fix-Linux-build.patch
new file mode 100644
index 00000000..1dc01f3f
--- /dev/null
+++ b/third_party/mDNSResponder/0001-Fix-Linux-build.patch
@@ -0,0 +1,32 @@
+From e136dcdcdd93ef32ada981e89c195905eb809eea Mon Sep 17 00:00:00 2001
+Message-ID: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+From: Nate Karstens <nate.karstens@garmin.com>
+Date: Thu, 23 Mar 2023 00:15:52 -0500
+Subject: [PATCH] Fix Linux build
+
+The __block qualifier is not used in Linux builds.
+
+Signed-off-by: Nate Karstens <nate.karstens@garmin.com>
+---
+ mDNSShared/uds_daemon.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/mDNSShared/uds_daemon.c b/mDNSShared/uds_daemon.c
+index 9ae5f78..5a00bb5 100644
+--- a/mDNSShared/uds_daemon.c
++++ b/mDNSShared/uds_daemon.c
+@@ -2912,7 +2912,11 @@ exit:
+ mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d)
+ {
+ browser_t *b, *p;
++#if defined(TARGET_OS_MAC) && TARGET_OS_MAC
+ __block mStatus err;
++#else
++ mStatus err;
++#endif
+
+ for (p = info->u.browser.browsers; p; p = p->next)
+ {
+--
+2.41.0
+
diff --git a/third_party/mDNSResponder/0002-Create-subroutine-for-cleaning-recent-interfaces.patch b/third_party/mDNSResponder/0002-Create-subroutine-for-cleaning-recent-interfaces.patch
new file mode 100644
index 00000000..98da74c5
--- /dev/null
+++ b/third_party/mDNSResponder/0002-Create-subroutine-for-cleaning-recent-interfaces.patch
@@ -0,0 +1,64 @@
+From 4f7970ac1615aba7a39ae94c1ca14135265574e9 Mon Sep 17 00:00:00 2001
+Message-ID: <4f7970ac1615aba7a39ae94c1ca14135265574e9.1687508149.git.stefan@agner.ch>
+In-Reply-To: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+References: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+From: Nate Karstens <nate.karstens@garmin.com>
+Date: Wed, 28 Jun 2017 17:30:00 -0500
+Subject: [PATCH] Create subroutine for cleaning recent interfaces
+
+Moves functionality for cleaning the list of recent
+interfaces into its own subroutine.
+
+Upstream-Status: Submitted [dts@apple.com]
+
+Signed-off-by: Nate Karstens <nate.karstens@garmin.com>
+Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
+---
+ mDNSPosix/mDNSPosix.c | 24 ++++++++++++++----------
+ 1 file changed, 14 insertions(+), 10 deletions(-)
+
+diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c
+index 0a7c3df..fe7242d 100644
+--- a/mDNSPosix/mDNSPosix.c
++++ b/mDNSPosix/mDNSPosix.c
+@@ -1322,6 +1322,19 @@ mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interf
+ return err;
+ }
+
++// Clean up any interfaces that have been hanging around on the RecentInterfaces list for more than a minute
++mDNSlocal void CleanRecentInterfaces(void)
++{
++ PosixNetworkInterface **ri = &gRecentInterfaces;
++ const mDNSs32 utc = mDNSPlatformUTC();
++ while (*ri)
++ {
++ PosixNetworkInterface *pi = *ri;
++ if (utc - pi->LastSeen < 60) ri = (PosixNetworkInterface **)&pi->coreIntf.next;
++ else { *ri = (PosixNetworkInterface *)pi->coreIntf.next; mdns_free(pi); }
++ }
++}
++
+ // Creates a PosixNetworkInterface for the interface whose IP address is
+ // intfAddr and whose name is intfName and registers it with mDNS core.
+ mDNSlocal int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, struct sockaddr *intfMask,
+@@ -1559,16 +1572,7 @@ mDNSlocal int SetupInterfaceList(mDNS *const m)
+
+ // Clean up.
+ if (intfList != NULL) freeifaddrs(intfList);
+-
+- // Clean up any interfaces that have been hanging around on the RecentInterfaces list for more than a minute
+- PosixNetworkInterface **ri = &gRecentInterfaces;
+- const mDNSs32 utc = mDNSPlatformUTC();
+- while (*ri)
+- {
+- PosixNetworkInterface *pi = *ri;
+- if (utc - pi->LastSeen < 60) ri = (PosixNetworkInterface **)&pi->coreIntf.next;
+- else { *ri = (PosixNetworkInterface *)pi->coreIntf.next; mdns_free(pi); }
+- }
++ CleanRecentInterfaces();
+
+ return err;
+ }
+--
+2.41.0
+
diff --git a/third_party/mDNSResponder/0003-Create-subroutine-for-tearing-down-an-interface.patch b/third_party/mDNSResponder/0003-Create-subroutine-for-tearing-down-an-interface.patch
new file mode 100644
index 00000000..812bd20c
--- /dev/null
+++ b/third_party/mDNSResponder/0003-Create-subroutine-for-tearing-down-an-interface.patch
@@ -0,0 +1,62 @@
+From f7ab91f739b936305ca56743adfb4673e3f2f4ba Mon Sep 17 00:00:00 2001
+Message-ID: <f7ab91f739b936305ca56743adfb4673e3f2f4ba.1687508149.git.stefan@agner.ch>
+In-Reply-To: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+References: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+From: Nate Karstens <nate.karstens@garmin.com>
+Date: Wed, 28 Jun 2017 17:30:00 -0500
+Subject: [PATCH] Create subroutine for tearing down an interface
+
+Creates a subroutine for tearing down an interface.
+
+Upstream-Status: Submitted [dts@apple.com]
+
+Signed-off-by: Nate Karstens <nate.karstens@garmin.com>
+Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
+---
+ mDNSPosix/mDNSPosix.c | 22 ++++++++++++++++------
+ 1 file changed, 16 insertions(+), 6 deletions(-)
+
+diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c
+index fe7242d..a32a880 100644
+--- a/mDNSPosix/mDNSPosix.c
++++ b/mDNSPosix/mDNSPosix.c
+@@ -1043,6 +1043,19 @@ mDNSlocal void FreePosixNetworkInterface(PosixNetworkInterface *intf)
+ gRecentInterfaces = intf;
+ }
+
++mDNSlocal void TearDownInterface(mDNS *const m, PosixNetworkInterface *intf)
++{
++ mDNS_DeregisterInterface(m, &intf->coreIntf, NormalActivation);
++ if (gMDNSPlatformPosixVerboseLevel > 0) fprintf(stderr, "Deregistered interface %s\n", intf->intfName);
++ FreePosixNetworkInterface(intf);
++
++ num_registered_interfaces--;
++ if (num_registered_interfaces == 0) {
++ num_pkts_accepted = 0;
++ num_pkts_rejected = 0;
++ }
++}
++
+ // Grab the first interface, deregister it, free it, and repeat until done.
+ mDNSlocal void ClearInterfaceList(mDNS *const m)
+ {
+@@ -1051,13 +1064,10 @@ mDNSlocal void ClearInterfaceList(mDNS *const m)
+ while (m->HostInterfaces)
+ {
+ PosixNetworkInterface *intf = (PosixNetworkInterface*)(m->HostInterfaces);
+- mDNS_DeregisterInterface(m, &intf->coreIntf, NormalActivation);
+- if (gMDNSPlatformPosixVerboseLevel > 0) fprintf(stderr, "Deregistered interface %s\n", intf->intfName);
+- FreePosixNetworkInterface(intf);
++ TearDownInterface(m, intf);
+ }
+- num_registered_interfaces = 0;
+- num_pkts_accepted = 0;
+- num_pkts_rejected = 0;
++
++ assert(num_registered_interfaces == 0);
+ }
+
+ mDNSlocal int SetupIPv6Socket(int fd)
+--
+2.41.0
+
diff --git a/third_party/mDNSResponder/0004-Track-interface-socket-family.patch b/third_party/mDNSResponder/0004-Track-interface-socket-family.patch
new file mode 100644
index 00000000..48fbc741
--- /dev/null
+++ b/third_party/mDNSResponder/0004-Track-interface-socket-family.patch
@@ -0,0 +1,54 @@
+From 542c1b2ce1dcc069cf848d11978c8b6ae5982b6e Mon Sep 17 00:00:00 2001
+Message-ID: <542c1b2ce1dcc069cf848d11978c8b6ae5982b6e.1687508149.git.stefan@agner.ch>
+In-Reply-To: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+References: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+From: Nate Karstens <nate.karstens@garmin.com>
+Date: Wed, 28 Jun 2017 17:30:00 -0500
+Subject: [PATCH] Track interface socket family
+
+Tracks the socket family associated with the interface.
+
+Upstream-Status: Submitted [dts@apple.com]
+
+Signed-off-by: Nate Karstens <nate.karstens@garmin.com>
+Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
+---
+ mDNSPosix/mDNSPosix.c | 1 +
+ mDNSPosix/mDNSPosix.h | 2 ++
+ 2 files changed, 3 insertions(+)
+
+diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c
+index a32a880..9a5b4d7 100644
+--- a/mDNSPosix/mDNSPosix.c
++++ b/mDNSPosix/mDNSPosix.c
+@@ -1415,6 +1415,7 @@ mDNSlocal int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, struct
+ // Set up the extra fields in PosixNetworkInterface.
+ assert(intf->intfName != NULL); // intf->intfName already set up above
+ intf->index = intfIndex;
++ intf->sa_family = intfAddr->sa_family;
+ intf->multicastSocket4 = -1;
+ #if HAVE_IPV6
+ intf->multicastSocket6 = -1;
+diff --git a/mDNSPosix/mDNSPosix.h b/mDNSPosix/mDNSPosix.h
+index 9675591..dd7864c 100644
+--- a/mDNSPosix/mDNSPosix.h
++++ b/mDNSPosix/mDNSPosix.h
+@@ -19,6 +19,7 @@
+ #define __mDNSPlatformPosix_h
+
+ #include <signal.h>
++#include <sys/socket.h>
+ #include <sys/time.h>
+
+ #ifdef __cplusplus
+@@ -40,6 +41,7 @@ struct PosixNetworkInterface
+ char * intfName;
+ PosixNetworkInterface * aliasIntf;
+ int index;
++ sa_family_t sa_family;
+ int multicastSocket4;
+ #if HAVE_IPV6
+ int multicastSocket6;
+--
+2.41.0
+
diff --git a/third_party/mDNSResponder/0005-Indicate-loopback-interface-to-mDNS-core.patch b/third_party/mDNSResponder/0005-Indicate-loopback-interface-to-mDNS-core.patch
new file mode 100644
index 00000000..f7aa4617
--- /dev/null
+++ b/third_party/mDNSResponder/0005-Indicate-loopback-interface-to-mDNS-core.patch
@@ -0,0 +1,61 @@
+From 44385771ef63f081ed7e80eae6f24591046b4c7c Mon Sep 17 00:00:00 2001
+Message-ID: <44385771ef63f081ed7e80eae6f24591046b4c7c.1687508149.git.stefan@agner.ch>
+In-Reply-To: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+References: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+From: Nate Karstens <nate.karstens@garmin.com>
+Date: Tue, 1 Aug 2017 17:06:01 -0500
+Subject: [PATCH] Indicate loopback interface to mDNS core
+
+Tells the mDNS core if an interface is a loopback interface,
+similar to AddInterfaceToList() in the MacOS implementation.
+
+Upstream-Status: Submitted [dts@apple.com]
+
+Signed-off-by: Nate Karstens <nate.karstens@garmin.com>
+Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
+---
+ mDNSPosix/mDNSPosix.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c
+index 9a5b4d7..02a19b4 100644
+--- a/mDNSPosix/mDNSPosix.c
++++ b/mDNSPosix/mDNSPosix.c
+@@ -1348,7 +1348,7 @@ mDNSlocal void CleanRecentInterfaces(void)
+ // Creates a PosixNetworkInterface for the interface whose IP address is
+ // intfAddr and whose name is intfName and registers it with mDNS core.
+ mDNSlocal int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, struct sockaddr *intfMask,
+- const mDNSu8 *intfHaddr, mDNSu16 intfHlen, const char *intfName, int intfIndex)
++ const mDNSu8 *intfHaddr, mDNSu16 intfHlen, const char *intfName, int intfIndex, int intfFlags)
+ {
+ int err = 0;
+ PosixNetworkInterface *intf;
+@@ -1411,6 +1411,7 @@ mDNSlocal int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, struct
+
+ intf->coreIntf.Advertise = m->AdvertiseLocalAddresses;
+ intf->coreIntf.McastTxRx = mDNStrue;
++ intf->coreIntf.Loopback = ((intfFlags & IFF_LOOPBACK) != 0) ? mDNStrue : mDNSfalse;
+
+ // Set up the extra fields in PosixNetworkInterface.
+ assert(intf->intfName != NULL); // intf->intfName already set up above
+@@ -1561,7 +1562,7 @@ mDNSlocal int SetupInterfaceList(mDNS *const m)
+ }
+ #endif
+ if (SetupOneInterface(m, i->ifa_addr, i->ifa_netmask,
+- hwaddr, hwaddr_len, i->ifa_name, ifIndex) == 0)
++ hwaddr, hwaddr_len, i->ifa_name, ifIndex, i->ifa_flags) == 0)
+ {
+ if (i->ifa_addr->sa_family == AF_INET)
+ foundav4 = mDNStrue;
+@@ -1578,7 +1579,7 @@ mDNSlocal int SetupInterfaceList(mDNS *const m)
+ // if ((m->HostInterfaces == NULL) && (firstLoopback != NULL))
+ if (!foundav4 && firstLoopback)
+ (void) SetupOneInterface(m, firstLoopback->ifa_addr, firstLoopback->ifa_netmask,
+- NULL, 0, firstLoopback->ifa_name, firstLoopbackIndex);
++ NULL, 0, firstLoopback->ifa_name, firstLoopbackIndex, firstLoopback->ifa_flags);
+ }
+
+ // Clean up.
+--
+2.41.0
+
diff --git a/third_party/mDNSResponder/0006-Use-list-for-changed-interfaces.patch b/third_party/mDNSResponder/0006-Use-list-for-changed-interfaces.patch
new file mode 100644
index 00000000..87ac1907
--- /dev/null
+++ b/third_party/mDNSResponder/0006-Use-list-for-changed-interfaces.patch
@@ -0,0 +1,178 @@
+From 2a0f873184068f21e1d0d2a3e0d8c26bc705bf88 Mon Sep 17 00:00:00 2001
+Message-ID: <2a0f873184068f21e1d0d2a3e0d8c26bc705bf88.1687508149.git.stefan@agner.ch>
+In-Reply-To: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+References: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+From: Nate Karstens <nate.karstens@garmin.com>
+Date: Thu, 13 Jul 2017 09:00:00 -0500
+Subject: [PATCH] Use list for changed interfaces
+
+Uses a linked list to store the index of changed network interfaces
+instead of a bitfield. This allows for network interfaces with an
+index greater than 31 (an index of 36 was seen on Android).
+
+Upstream-Status: Submitted [dts@apple.com]
+
+Signed-off-by: Nate Karstens <nate.karstens@garmin.com>
+Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
+Change-Id: Ibeab0ec68ca0d21da8384d4362e59afd2951f138
+---
+ mDNSPosix/mDNSPosix.c | 60 +++++++++++++++++++++++++++++++------------
+ 1 file changed, 44 insertions(+), 16 deletions(-)
+
+diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c
+index 02a19b4..9867881 100644
+--- a/mDNSPosix/mDNSPosix.c
++++ b/mDNSPosix/mDNSPosix.c
+@@ -74,6 +74,14 @@ struct IfChangeRec
+ };
+ typedef struct IfChangeRec IfChangeRec;
+
++// Used to build a list of network interface indices
++struct NetworkInterfaceIndex
++{
++ int if_index;
++ struct NetworkInterfaceIndex *Next;
++};
++typedef struct NetworkInterfaceIndex NetworkInterfaceIndex;
++
+ // Note that static data is initialized to zero in (modern) C.
+ static PosixEventSource *gEventSources; // linked list of PosixEventSource's
+ static sigset_t gEventSignalSet; // Signals which event loop listens for
+@@ -1621,6 +1629,23 @@ mDNSlocal mStatus OpenIfNotifySocket(int *pFD)
+ return err;
+ }
+
++mDNSlocal void AddInterfaceIndexToList(GenLinkedList *list, int if_index)
++{
++ NetworkInterfaceIndex *item;
++
++ for (item = (NetworkInterfaceIndex*)list->Head; item != NULL; item = item->Next)
++ {
++ if (if_index == item->if_index) return;
++ }
++
++ item = mdns_malloc(sizeof *item);
++ if (item == NULL) return;
++
++ item->if_index = if_index;
++ item->Next = NULL;
++ AddToTail(list, item);
++}
++
+ #if MDNS_DEBUGMSGS
+ mDNSlocal void PrintNetLinkMsg(const struct nlmsghdr *pNLMsg)
+ {
+@@ -1648,14 +1673,13 @@ mDNSlocal void PrintNetLinkMsg(const struct nlmsghdr *pNLMsg)
+ }
+ #endif
+
+-mDNSlocal mDNSu32 ProcessRoutingNotification(int sd)
++mDNSlocal void ProcessRoutingNotification(int sd, GenLinkedList *changedInterfaces)
+ // Read through the messages on sd and if any indicate that any interface records should
+ // be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0.
+ {
+ ssize_t readCount;
+ char buff[4096];
+ struct nlmsghdr *pNLMsg = (struct nlmsghdr*) buff;
+- mDNSu32 result = 0;
+
+ // The structure here is more complex than it really ought to be because,
+ // unfortunately, there's no good way to size a buffer in advance large
+@@ -1691,9 +1715,9 @@ mDNSlocal mDNSu32 ProcessRoutingNotification(int sd)
+
+ // Process the NetLink message
+ if (pNLMsg->nlmsg_type == RTM_GETLINK || pNLMsg->nlmsg_type == RTM_NEWLINK)
+- result |= 1 << ((struct ifinfomsg*) NLMSG_DATA(pNLMsg))->ifi_index;
++ AddInterfaceIndexToList(changedInterfaces, ((struct ifinfomsg*) NLMSG_DATA(pNLMsg))->ifi_index);
+ else if (pNLMsg->nlmsg_type == RTM_DELADDR || pNLMsg->nlmsg_type == RTM_NEWADDR)
+- result |= 1 << ((struct ifaddrmsg*) NLMSG_DATA(pNLMsg))->ifa_index;
++ AddInterfaceIndexToList(changedInterfaces, ((struct ifaddrmsg*) NLMSG_DATA(pNLMsg))->ifa_index);
+
+ // Advance pNLMsg to the next message in the buffer
+ if ((pNLMsg->nlmsg_flags & NLM_F_MULTI) != 0 && pNLMsg->nlmsg_type != NLMSG_DONE)
+@@ -1704,8 +1728,6 @@ mDNSlocal mDNSu32 ProcessRoutingNotification(int sd)
+ else
+ break; // all done!
+ }
+-
+- return result;
+ }
+
+ #else // USES_NETLINK
+@@ -1737,18 +1759,17 @@ mDNSlocal void PrintRoutingSocketMsg(const struct ifa_msghdr *pRSMsg)
+ }
+ #endif
+
+-mDNSlocal mDNSu32 ProcessRoutingNotification(int sd)
++mDNSlocal void ProcessRoutingNotification(int sd, GenLinkedList *changedInterfaces)
+ // Read through the messages on sd and if any indicate that any interface records should
+ // be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0.
+ {
+ ssize_t readCount;
+ char buff[4096];
+ struct ifa_msghdr *pRSMsg = (struct ifa_msghdr*) buff;
+- mDNSu32 result = 0;
+
+ readCount = read(sd, buff, sizeof buff);
+ if (readCount < (ssize_t) sizeof(struct ifa_msghdr))
+- return mStatus_UnsupportedErr; // cannot decipher message
++ return; // cannot decipher message
+
+ #if MDNS_DEBUGMSGS
+ PrintRoutingSocketMsg(pRSMsg);
+@@ -1759,12 +1780,10 @@ mDNSlocal mDNSu32 ProcessRoutingNotification(int sd)
+ pRSMsg->ifam_type == RTM_IFINFO)
+ {
+ if (pRSMsg->ifam_type == RTM_IFINFO)
+- result |= 1 << ((struct if_msghdr*) pRSMsg)->ifm_index;
++ AddInterfaceIndexToList(changedInterfaces, ((struct if_msghdr*) pRSMsg)->ifm_index);
+ else
+- result |= 1 << pRSMsg->ifam_index;
++ AddInterfaceIndexToList(changedInterfaces, pRSMsg->ifam_index);
+ }
+-
+- return result;
+ }
+
+ #endif // USES_NETLINK
+@@ -1774,7 +1793,8 @@ mDNSlocal void InterfaceChangeCallback(int fd, void *context)
+ {
+ IfChangeRec *pChgRec = (IfChangeRec*) context;
+ fd_set readFDs;
+- mDNSu32 changedInterfaces = 0;
++ GenLinkedList changedInterfaces;
++ NetworkInterfaceIndex *changedInterface;
+ struct timeval zeroTimeout = { 0, 0 };
+
+ (void)fd; // Unused
+@@ -1782,17 +1802,25 @@ mDNSlocal void InterfaceChangeCallback(int fd, void *context)
+ FD_ZERO(&readFDs);
+ FD_SET(pChgRec->NotifySD, &readFDs);
+
++ InitLinkedList(&changedInterfaces, offsetof(NetworkInterfaceIndex, Next));
++
+ do
+ {
+- changedInterfaces |= ProcessRoutingNotification(pChgRec->NotifySD);
++ ProcessRoutingNotification(pChgRec->NotifySD, &changedInterfaces);
+ }
+ while (0 < select(pChgRec->NotifySD + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout));
+
+ // Currently we rebuild the entire interface list whenever any interface change is
+ // detected. If this ever proves to be a performance issue in a multi-homed
+ // configuration, more care should be paid to changedInterfaces.
+- if (changedInterfaces)
++ if (changedInterfaces.Head != NULL)
+ mDNSPlatformPosixRefreshInterfaceList(pChgRec->mDNS);
++
++ while ((changedInterface = (NetworkInterfaceIndex*)changedInterfaces.Head) != NULL)
++ {
++ RemoveFromList(&changedInterfaces, changedInterface);
++ mdns_free(changedInterface);
++ }
+ }
+
+ // Register with either a Routing Socket or RtNetLink to listen for interface changes.
+--
+2.41.0
+
diff --git a/third_party/mDNSResponder/0007-Handle-noisy-netlink-sockets.patch b/third_party/mDNSResponder/0007-Handle-noisy-netlink-sockets.patch
new file mode 100644
index 00000000..08cce016
--- /dev/null
+++ b/third_party/mDNSResponder/0007-Handle-noisy-netlink-sockets.patch
@@ -0,0 +1,255 @@
+From 00289e89cccb9567d6ea6bd2a394fd14b61e5ad1 Mon Sep 17 00:00:00 2001
+Message-ID: <00289e89cccb9567d6ea6bd2a394fd14b61e5ad1.1687508149.git.stefan@agner.ch>
+In-Reply-To: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+References: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+From: Nate Karstens <nate.karstens@garmin.com>
+Date: Mon, 24 Jul 2017 09:38:55 -0500
+Subject: [PATCH] Handle noisy netlink sockets
+
+The POSIX implementation currently clears all network interfaces
+when netlink indicates that there has been a change. This causes
+the following problems:
+
+ 1) Applications are informed that all of the services they are
+ tracking have been removed.
+ 2) Increases network load because the client must re-query for
+ all records it is interested in.
+
+This changes netlink notification handling by:
+
+ 1) Always comparing with the latest interface list returned
+ by the OS.
+ 2) Confirming that the interface has been changed in a way
+ that we care about.
+
+Upstream-Status: Submitted [dts@apple.com]
+
+Signed-off-by: Nate Karstens <nate.karstens@garmin.com>
+Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
+---
+ mDNSPosix/mDNSPosix.c | 182 +++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 172 insertions(+), 10 deletions(-)
+
+diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c
+index 9867881..ad7000d 100644
+--- a/mDNSPosix/mDNSPosix.c
++++ b/mDNSPosix/mDNSPosix.c
+@@ -1788,14 +1788,43 @@ mDNSlocal void ProcessRoutingNotification(int sd, GenLinkedList *change
+
+ #endif // USES_NETLINK
+
++// Test whether the given PosixNetworkInterface matches the given struct ifaddrs
++mDNSlocal mDNSBool InterfacesMatch(PosixNetworkInterface *intf, struct ifaddrs *ifi)
++{
++ mDNSBool match = mDNSfalse;
++ mDNSAddr ip, mask;
++ int if_index;
++
++ if_index = if_nametoindex(ifi->ifa_name);
++ if (if_index == 0)
++ return mDNSfalse;
++
++ if((intf->index == if_index) &&
++ (intf->sa_family == ifi->ifa_addr->sa_family) &&
++ (strcmp(intf->coreIntf.ifname, ifi->ifa_name) == 0))
++ {
++ SockAddrTomDNSAddr(ifi->ifa_addr, &ip, NULL);
++ SockAddrTomDNSAddr(ifi->ifa_netmask, &mask, NULL);
++
++ match = mDNSSameAddress(&intf->coreIntf.ip, &ip) &&
++ mDNSSameAddress(&intf->coreIntf.mask, &mask);
++ }
++
++ return match;
++}
++
+ // Called when data appears on interface change notification socket
+ mDNSlocal void InterfaceChangeCallback(int fd, void *context)
+ {
+ IfChangeRec *pChgRec = (IfChangeRec*) context;
++ mDNS *m = pChgRec->mDNS;
+ fd_set readFDs;
+ GenLinkedList changedInterfaces;
+ NetworkInterfaceIndex *changedInterface;
+ struct timeval zeroTimeout = { 0, 0 };
++ struct ifaddrs *ifa_list, **ifi, *ifa_loop4 = NULL;
++ PosixNetworkInterface *intf, *intfNext;
++ mDNSBool found, foundav4;
+
+ (void)fd; // Unused
+
+@@ -1810,12 +1839,149 @@ mDNSlocal void InterfaceChangeCallback(int fd, void *context)
+ }
+ while (0 < select(pChgRec->NotifySD + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout));
+
+- // Currently we rebuild the entire interface list whenever any interface change is
+- // detected. If this ever proves to be a performance issue in a multi-homed
+- // configuration, more care should be paid to changedInterfaces.
+- if (changedInterfaces.Head != NULL)
+- mDNSPlatformPosixRefreshInterfaceList(pChgRec->mDNS);
++ CleanRecentInterfaces();
++
++ if (changedInterfaces.Head == NULL) goto cleanup;
++
++ if (getifaddrs(&ifa_list) < 0) goto cleanup;
++
++ for (intf = (PosixNetworkInterface*)(m->HostInterfaces); intf != NULL; intf = intfNext)
++ {
++ intfNext = (PosixNetworkInterface*)(intf->coreIntf.next);
++
++ // Loopback interface(s) are handled later
++ if (intf->coreIntf.Loopback) continue;
++
++ found = mDNSfalse;
++ for (ifi = &ifa_list; *ifi != NULL; ifi = &(*ifi)->ifa_next)
++ {
++ if (InterfacesMatch(intf, *ifi))
++ {
++ found = mDNStrue;
++ break;
++ }
++ }
++
++ // Removes changed and old interfaces from m->HostInterfaces
++ if (!found) TearDownInterface(m, intf);
++ }
++
++ // Add new and changed interfaces in ifa_list
++ // Save off loopback interface in case it is needed later
++ for (ifi = &ifa_list; *ifi != NULL; ifi = &(*ifi)->ifa_next)
++ {
++ found = mDNSfalse;
++ for (intf = (PosixNetworkInterface*)(m->HostInterfaces); intf != NULL; intf = intfNext)
++ {
++ intfNext = (PosixNetworkInterface*)(intf->coreIntf.next);
++
++ // Loopback interface(s) are handled later
++ if (intf->coreIntf.Loopback) continue;
++
++ if (InterfacesMatch(intf, *ifi))
++ {
++ found = mDNStrue;
++ break;
++ }
++
++ // Removes changed and old interfaces from m->HostInterfaces
++ }
++ if (found)
++ continue;
++
++ if ((ifa_loop4 == NULL) &&
++ ((*ifi)->ifa_addr->sa_family == AF_INET) &&
++ ((*ifi)->ifa_flags & IFF_UP) &&
++ ((*ifi)->ifa_flags & IFF_LOOPBACK))
++ {
++ ifa_loop4 = *ifi;
++ continue;
++ }
++
++ if ( (((*ifi)->ifa_addr->sa_family == AF_INET)
++#if HAVE_IPV6
++ || ((*ifi)->ifa_addr->sa_family == AF_INET6)
++#endif
++ ) && ((*ifi)->ifa_flags & IFF_UP)
++ && !((*ifi)->ifa_flags & IFF_POINTOPOINT)
++ && !((*ifi)->ifa_flags & IFF_LOOPBACK))
++ {
++ struct ifaddrs *i = *ifi;
++
++#define ethernet_addr_len 6
++ uint8_t hwaddr[ethernet_addr_len];
++ int hwaddr_len = 0;
++
++#if defined(TARGET_OS_LINUX) && TARGET_OS_LINUX
++ struct ifreq ifr;
++ int sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
++ if (sockfd >= 0)
++ {
++ /* Add hardware address */
++ memcpy(ifr.ifr_name, i->ifa_name, IFNAMSIZ);
++ if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) != -1)
++ {
++ if (ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER)
++ {
++ memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ethernet_addr_len);
++ hwaddr_len = ethernet_addr_len;
++ }
++ }
++ close(sockfd);
++ }
++ else
++ {
++ memset(hwaddr, 0, sizeof(hwaddr));
++ }
++#endif // TARGET_OS_LINUX
++ SetupOneInterface(m, i->ifa_addr, i->ifa_netmask,
++ hwaddr, hwaddr_len, i->ifa_name, if_nametoindex(i->ifa_name), i->ifa_flags);
++ }
++ }
++
++ // Determine if there is at least one non-loopback IPv4 interface. This is to work around issues
++ // with multicast loopback on IPv6 interfaces -- see corresponding logic in SetupInterfaceList().
++ foundav4 = mDNSfalse;
++ for (intf = (PosixNetworkInterface*)(m->HostInterfaces); intf != NULL; intf = (PosixNetworkInterface*)(intf->coreIntf.next))
++ {
++ if (intf->sa_family == AF_INET && !intf->coreIntf.Loopback)
++ {
++ foundav4 = mDNStrue;
++ break;
++ }
++ }
++
++ if (foundav4)
++ {
++ for (intf = (PosixNetworkInterface*)(m->HostInterfaces); intf != NULL; intf = intfNext)
++ {
++ intfNext = (PosixNetworkInterface*)(intf->coreIntf.next);
++ if (intf->coreIntf.Loopback) TearDownInterface(m, intf);
++ }
++ }
++ else
++ {
++ found = mDNSfalse;
++
++ for (intf = (PosixNetworkInterface*)(m->HostInterfaces); intf != NULL; intf = (PosixNetworkInterface*)(intf->coreIntf.next))
++ {
++ if (intf->coreIntf.Loopback)
++ {
++ found = mDNStrue;
++ break;
++ }
++ }
++
++ if (!found && (ifa_loop4 != NULL))
++ {
++ SetupOneInterface(m, ifa_loop4->ifa_addr, ifa_loop4->ifa_netmask,
++ NULL, 0, ifa_loop4->ifa_name, if_nametoindex(ifa_loop4->ifa_name), ifa_loop4->ifa_flags);
++ }
++ }
++
++ if (ifa_list != NULL) freeifaddrs(ifa_list);
+
++cleanup:
+ while ((changedInterface = (NetworkInterfaceIndex*)changedInterfaces.Head) != NULL)
+ {
+ RemoveFromList(&changedInterfaces, changedInterface);
+@@ -1947,15 +2113,11 @@ mDNSexport void mDNSPlatformClose(mDNS *const m)
+ #endif
+ }
+
+-// This is used internally by InterfaceChangeCallback.
+-// It's also exported so that the Standalone Responder (mDNSResponderPosix)
++// This is exported so that the Standalone Responder (mDNSResponderPosix)
+ // can call it in response to a SIGHUP (mainly for debugging purposes).
+ mDNSexport mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m)
+ {
+ int err;
+- // This is a pretty heavyweight way to process interface changes --
+- // destroying the entire interface list and then making fresh one from scratch.
+- // We should make it like the OS X version, which leaves unchanged interfaces alone.
+ ClearInterfaceList(m);
+ err = SetupInterfaceList(m);
+ return PosixErrorToStatus(err);
+--
+2.41.0
+
diff --git a/third_party/mDNSResponder/0008-Mark-deleted-interfaces-as-being-changed.patch b/third_party/mDNSResponder/0008-Mark-deleted-interfaces-as-being-changed.patch
new file mode 100644
index 00000000..216fde7f
--- /dev/null
+++ b/third_party/mDNSResponder/0008-Mark-deleted-interfaces-as-being-changed.patch
@@ -0,0 +1,43 @@
+From 8ebfeaf55ab364a1e51a3438dfa9a742a01b8d36 Mon Sep 17 00:00:00 2001
+Message-ID: <8ebfeaf55ab364a1e51a3438dfa9a742a01b8d36.1687508149.git.stefan@agner.ch>
+In-Reply-To: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+References: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+From: Nate Karstens <nate.karstens@garmin.com>
+Date: Wed, 9 Aug 2017 09:16:58 -0500
+Subject: [PATCH] Mark deleted interfaces as being changed
+
+Netlink notification handling ignores messages for deleted links,
+RTM_DELLINK. It does handle RTM_GETLINK. According to libnl docu-
+mentation (http://www.infradead.org/~tgr/libnl/doc/route.html)
+RTM_DELLINK can be sent by the kernel, but RTM_GETLINK cannot.
+There was likely a mixup in the original implementation, so this
+change replaces handling for RTM_GETLINK with RTM_DELLINK.
+
+Testing and Verification Instructions:
+ 1. Use ip-link to add and remove a VLAN interface and verify
+ that mDNSResponder handles the deleted link.
+
+Upstream-Status: Submitted [dts@apple.com]
+
+Signed-off-by: Nate Karstens <nate.karstens@garmin.com>
+Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
+---
+ mDNSPosix/mDNSPosix.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c
+index ad7000d..010f266 100644
+--- a/mDNSPosix/mDNSPosix.c
++++ b/mDNSPosix/mDNSPosix.c
+@@ -1714,7 +1714,7 @@ mDNSlocal void ProcessRoutingNotification(int sd, GenLinkedList *change
+ #endif
+
+ // Process the NetLink message
+- if (pNLMsg->nlmsg_type == RTM_GETLINK || pNLMsg->nlmsg_type == RTM_NEWLINK)
++ if (pNLMsg->nlmsg_type == RTM_DELLINK || pNLMsg->nlmsg_type == RTM_NEWLINK)
+ AddInterfaceIndexToList(changedInterfaces, ((struct ifinfomsg*) NLMSG_DATA(pNLMsg))->ifi_index);
+ else if (pNLMsg->nlmsg_type == RTM_DELADDR || pNLMsg->nlmsg_type == RTM_NEWADDR)
+ AddInterfaceIndexToList(changedInterfaces, ((struct ifaddrmsg*) NLMSG_DATA(pNLMsg))->ifa_index);
+--
+2.41.0
+
diff --git a/third_party/mDNSResponder/0009-Handle-errors-from-socket-calls.patch b/third_party/mDNSResponder/0009-Handle-errors-from-socket-calls.patch
new file mode 100644
index 00000000..2057e2cb
--- /dev/null
+++ b/third_party/mDNSResponder/0009-Handle-errors-from-socket-calls.patch
@@ -0,0 +1,66 @@
+From dae89c4e97faf408394961c0f4b1577a7d5976cc Mon Sep 17 00:00:00 2001
+Message-ID: <dae89c4e97faf408394961c0f4b1577a7d5976cc.1687508149.git.stefan@agner.ch>
+In-Reply-To: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+References: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+From: Nate Karstens <nate.karstens@garmin.com>
+Date: Thu, 10 Aug 2017 08:27:32 -0500
+Subject: [PATCH] Handle errors from socket calls
+
+Adds handling for socket() or read() returning a
+negative value (indicating an error has occurred).
+
+Upstream-Status: Submitted [dts@apple.com]
+
+Signed-off-by: Nate Karstens <nate.karstens@garmin.com>
+Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com>
+---
+ mDNSPosix/mDNSPosix.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c
+index 010f266..89e108f 100644
+--- a/mDNSPosix/mDNSPosix.c
++++ b/mDNSPosix/mDNSPosix.c
+@@ -1677,7 +1677,7 @@ mDNSlocal void ProcessRoutingNotification(int sd, GenLinkedList *change
+ // Read through the messages on sd and if any indicate that any interface records should
+ // be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0.
+ {
+- ssize_t readCount;
++ ssize_t readVal, readCount;
+ char buff[4096];
+ struct nlmsghdr *pNLMsg = (struct nlmsghdr*) buff;
+
+@@ -1686,7 +1686,10 @@ mDNSlocal void ProcessRoutingNotification(int sd, GenLinkedList *change
+ // enough to hold all pending data and so avoid message fragmentation.
+ // (Note that FIONREAD is not supported on AF_NETLINK.)
+
+- readCount = read(sd, buff, sizeof buff);
++ readVal = read(sd, buff, sizeof buff);
++ if (readVal < 0) return;
++ readCount = readVal;
++
+ while (1)
+ {
+ // Make sure we've got an entire nlmsghdr in the buffer, and payload, too.
+@@ -1702,7 +1705,9 @@ mDNSlocal void ProcessRoutingNotification(int sd, GenLinkedList *change
+ pNLMsg = (struct nlmsghdr*) buff;
+
+ // read more data
+- readCount += read(sd, buff + readCount, sizeof buff - readCount);
++ readVal = read(sd, buff + readCount, sizeof buff - readCount);
++ if (readVal < 0) return;
++ readCount += readVal;
+ continue; // spin around and revalidate with new readCount
+ }
+ else
+@@ -2017,6 +2022,7 @@ mDNSlocal mDNSBool mDNSPlatformInit_CanReceiveUnicast(void)
+ int err;
+ int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ struct sockaddr_in s5353;
++ if (s < 0) return mDNSfalse;
+ s5353.sin_family = AF_INET;
+ s5353.sin_port = MulticastDNSPort.NotAnInteger;
+ s5353.sin_addr.s_addr = 0;
+--
+2.41.0
+
diff --git a/third_party/mDNSResponder/0010-Handle-interface-without-ifa_addr.patch b/third_party/mDNSResponder/0010-Handle-interface-without-ifa_addr.patch
new file mode 100644
index 00000000..602b205e
--- /dev/null
+++ b/third_party/mDNSResponder/0010-Handle-interface-without-ifa_addr.patch
@@ -0,0 +1,41 @@
+From e501d58e9ec6cb6e19a682d425fa638069585fbc Mon Sep 17 00:00:00 2001
+Message-ID: <e501d58e9ec6cb6e19a682d425fa638069585fbc.1687508149.git.stefan@agner.ch>
+In-Reply-To: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+References: <e136dcdcdd93ef32ada981e89c195905eb809eea.1687508149.git.stefan@agner.ch>
+From: Stefan Agner <stefan@agner.ch>
+Date: Fri, 23 Jun 2023 10:10:00 +0200
+Subject: [PATCH] Handle interface without `ifa_addr`
+
+It seems that certain interface types may have `ifa_addr` set to null.
+Handle this case gracefully.
+
+Signed-off-by: Stefan Agner <stefan@agner.ch>
+---
+ mDNSPosix/mDNSPosix.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c
+index 89e108f..2056871 100644
+--- a/mDNSPosix/mDNSPosix.c
++++ b/mDNSPosix/mDNSPosix.c
+@@ -1895,6 +1895,7 @@ mDNSlocal void InterfaceChangeCallback(int fd, void *context)
+ continue;
+
+ if ((ifa_loop4 == NULL) &&
++ ((*ifi)->ifa_addr != NULL) &&
+ ((*ifi)->ifa_addr->sa_family == AF_INET) &&
+ ((*ifi)->ifa_flags & IFF_UP) &&
+ ((*ifi)->ifa_flags & IFF_LOOPBACK))
+@@ -1903,7 +1904,8 @@ mDNSlocal void InterfaceChangeCallback(int fd, void *context)
+ continue;
+ }
+
+- if ( (((*ifi)->ifa_addr->sa_family == AF_INET)
++ if ( ((*ifi)->ifa_addr != NULL) &&
++ (((*ifi)->ifa_addr->sa_family == AF_INET)
+ #if HAVE_IPV6
+ || ((*ifi)->ifa_addr->sa_family == AF_INET6)
+ #endif
+--
+2.41.0
+
diff --git a/third_party/openthread/CMakeLists.txt b/third_party/openthread/CMakeLists.txt
index af8ddf49..03ad8633 100644
--- a/third_party/openthread/CMakeLists.txt
+++ b/third_party/openthread/CMakeLists.txt
@@ -35,6 +35,7 @@ set(OT_BORDER_AGENT_ID ON CACHE STRING "enable border agent ID" FORCE)
set(OT_BORDER_ROUTER ON CACHE STRING "enable border router feature" FORCE)
set(OT_BORDER_ROUTING ${OTBR_BORDER_ROUTING} CACHE STRING "enable border routing feature" FORCE)
set(OT_BORDER_ROUTING_COUNTERS ${OTBR_BORDER_ROUTING_COUNTERS} CACHE STRING "enable border routing counters feature" FORCE)
+set(OT_BORDER_ROUTING_DHCP6_PD ${OTBR_DHCP6_PD} CACHE STRING "enable dhcpv6 pd support in border routing" FORCE)
set(OT_BUILD_EXECUTABLES OFF CACHE STRING "disable building executables" FORCE)
set(OT_BUILTIN_MBEDTLS_MANAGEMENT OFF CACHE STRING "diable mbedTLS management" FORCE)
set(OT_CHILD_SUPERVISION ON CACHE STRING "enable child supervision" FORCE)