diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-10-14 12:08:40 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-10-14 12:08:40 +0000 |
commit | 5a1cccb7afbec08a2d0145fa243836638c2d194e (patch) | |
tree | 994a922ec4ce28a3c01fdc03b91888ef25f9da95 | |
parent | ec2745badf10cb6560aeaefafe8f92fb508007f0 (diff) | |
parent | 6d7c9418cdf6ab0b2674cb553c3f1176204827f5 (diff) | |
download | ot-br-posix-5a1cccb7afbec08a2d0145fa243836638c2d194e.tar.gz |
Snap for 10950907 from 6d7c9418cdf6ab0b2674cb553c3f1176204827f5 to mainline-media-swcodec-release
Change-Id: I62f6c26c2cca7a0f6c36162961f48277860a3480
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) |