From 69877768d18ea3594c852e3bcdf42083993c28da Mon Sep 17 00:00:00 2001 From: Christian Glombek Date: Wed, 2 Sep 2026 06:26:36 +0200 Subject: [PATCH 1/4] openthread-br: update to 2026.09.0 Update from a 2025-06-12 snapshot to the v2026.09.0 release and switch to release-tag versioning. The in-tree LuCI app is gone: upstream removed it in this release (openthread/ot-br-posix#3326), so the luci-app-openthread package cannot be built from this source any more. A maintained replacement is proposed as a proper LuCI app in openwrt/luci#8871. OTBR_VENDOR_NAME and OTBR_PRODUCT_NAME are now required (the 2026.08.0 release removed the built-in defaults) and are set to the values those defaults had, so existing users' border routers keep their MeshCoP instance name. OTBR_VERSION is set so --version reports the release rather than the CMake project version; see the README for the reasoning on both. The gcc14 build fix is still needed: the bundled mbedtls still lacks the upstream fix at this release's submodule pins. Three CMake options also leave the invocation: OTBR_DUA_ROUTING no longer exists upstream, and OTBR_DNSSD_DISCOVERY_PROXY and OTBR_SRP_ADVERTISING_PROXY now default appropriately from OTBR_MDNS (forcing them ON alongside the OT-core proxies is a CMake FATAL_ERROR once the internal mDNS is selected). Also add a functional smoke test. Signed-off-by: Christian Glombek Assisted-By: AI --- net/openthread-br/Makefile | 41 +++-------- net/openthread-br/README.md | 30 +++++++- ...-to-externally-provide-cJSON-library.patch | 56 --------------- .../patches/102-openwrt-fix-LuCI-icons.patch | 68 ------------------- net/openthread-br/test.sh | 42 ++++++++++++ 5 files changed, 79 insertions(+), 158 deletions(-) delete mode 100644 net/openthread-br/patches/101-rest-Permit-to-externally-provide-cJSON-library.patch delete mode 100644 net/openthread-br/patches/102-openwrt-fix-LuCI-icons.patch create mode 100644 net/openthread-br/test.sh diff --git a/net/openthread-br/Makefile b/net/openthread-br/Makefile index 948668545bc06f..ff1459a34ffb34 100644 --- a/net/openthread-br/Makefile +++ b/net/openthread-br/Makefile @@ -4,13 +4,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=openthread-br -PKG_SOURCE_DATE:=2025-06-12 -PKG_SOURCE_VERSION:=2f3c799c7463c8f674754e65c53f78bc0bbcbd58 -PKG_RELEASE:=2 +PKG_VERSION:=2026.09.0 +PKG_SOURCE_VERSION:=v$(PKG_VERSION) +PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=https://github.com/openthread/ot-br-posix.git -PKG_MIRROR_HASH:=fae1c7c88102f88bac32f8ee25e044b9a8781eef6b9acef39388e1e11a44a195 +PKG_MIRROR_HASH:=d0c82691b95e0aa2112ca08f6ff544c23794eae632c8c01b040616a0d60f5aab PKG_MAINTAINER:=Stijn Tintel PKG_LICENSE:=BSD-3-Clause @@ -19,14 +19,6 @@ PKG_LICENSE_FILES:=LICENSE include $(INCLUDE_DIR)/package.mk include $(INCLUDE_DIR)/cmake.mk -define Package/luci-app-openthread - CATEGORY:=LuCI - SECTION:=luci - SUBMENU:=3. Applications - TITLE:=LuCI Support for OpenThread Border Router - DEPENDS:=+luci-base +luci-lua-runtime -endef - define Package/openthread-br CATEGORY:=Network SECTION:=net @@ -63,34 +55,18 @@ CMAKE_OPTIONS += \ -DOT_TARGET_OPENWRT=ON \ -DOTBR_BACKBONE_ROUTER=ON \ -DOTBR_BORDER_ROUTING=ON \ - -DOTBR_DNSSD_DISCOVERY_PROXY=ON \ - -DOTBR_DUA_ROUTING=ON \ -DOTBR_MDNS=mDNSResponder \ -DOTBR_NAT64=OFF \ -DOTBR_OPENWRT=ON \ + -DOTBR_PRODUCT_NAME=BorderRouter \ -DOTBR_REST=ON \ - -DOTBR_SRP_ADVERTISING_PROXY=ON \ -DOTBR_SRP_SERVER_AUTO_ENABLE=ON \ - -DOTBR_TREL=ON + -DOTBR_TREL=ON \ + -DOTBR_VENDOR_NAME=OpenThread \ + -DOTBR_VERSION=$(PKG_VERSION) TARGET_CFLAGS += -DOPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME=\\\"/var/run/openthread-%s\\\" -define Package/luci-app-openthread/install - $(INSTALL_DIR) \ - $(1)/usr/lib/lua/luci/controller/admin \ - $(1)/usr/lib/lua/luci/view/admin_thread \ - $(1)/www/luci-static/resources - $(INSTALL_DATA) \ - $(PKG_BUILD_DIR)/src/openwrt/controller/thread.lua \ - $(1)/usr/lib/lua/luci/controller/admin - $(INSTALL_DATA) \ - $(PKG_BUILD_DIR)/src/openwrt/view/admin_thread/* \ - $(1)/usr/lib/lua/luci/view/admin_thread - $(INSTALL_DATA) \ - $(PKG_BUILD_DIR)/src/openwrt/handle_error.js \ - $(1)/www/luci-static/resources -endef - define Package/openthread-br/install $(INSTALL_DIR) \ $(1)/etc/init.d \ @@ -101,5 +77,4 @@ define Package/openthread-br/install endef -$(eval $(call BuildPackage,luci-app-openthread)) $(eval $(call BuildPackage,openthread-br)) diff --git a/net/openthread-br/README.md b/net/openthread-br/README.md index e4a26d427ba714..99aea2459b2660 100644 --- a/net/openthread-br/README.md +++ b/net/openthread-br/README.md @@ -25,6 +25,30 @@ the package will likely result in more bug reports. As the package and its dependencies are unlikely to fit in any router with small flash (16MB or less), I don't see much point in making things configurable for reducing size either. +### Vendor and product name + +`OTBR_VENDOR_NAME` and `OTBR_PRODUCT_NAME` have to be set: the 2026.08.0 release removed +the built-in defaults, and otbr-agent exits with `Vendor name must be set.` +without them. + +They are deliberately set to the values those defaults had, `OpenThread` and +`BorderRouter`, rather than to something OpenWrt specific. The pair forms the +MeshCoP service instance name as ` `, which is the name shown +when adding the border router in a Thread client and the one already-paired +clients have recorded, so changing it would rename every existing user's border +router on upgrade. + +### Version string + +`OTBR_VERSION` is set to `PKG_VERSION`. Without it the build falls back to the +CMake project version, because the repacked source tree has no git directory +for `git describe` to read, so `otbr-agent --version` and the `Running ...` +line it logs on every start would report `0.3.0` rather than the release the +package was built from. + +The version test in the package CI matches on that string, so dropping this +option would make the package fail it again. + ### Firewall support OpenWrt uses firewall4 with nftables by default, but the OpenThread firewall @@ -125,7 +149,11 @@ ubus call otbr threadstop ### LuCI -Creating a network in LuCI appears to be broken for the moment. +This package no longer ships a LuCI application; the web UI lives in the +`luci-app-openthread` package in the openwrt/luci repository +(https://github.com/openwrt/luci/pull/8871). Install that alongside this package +for a web interface, on a LuCI feed recent enough to carry it — older feeds will +not have the package yet. ### CLI diff --git a/net/openthread-br/patches/101-rest-Permit-to-externally-provide-cJSON-library.patch b/net/openthread-br/patches/101-rest-Permit-to-externally-provide-cJSON-library.patch deleted file mode 100644 index 812978c3350966..00000000000000 --- a/net/openthread-br/patches/101-rest-Permit-to-externally-provide-cJSON-library.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 59c389a91cfe91eebed9c36887fa2a3eca4cbd6f Mon Sep 17 00:00:00 2001 -From: Christian Marangi -Date: Mon, 24 Nov 2025 18:53:28 +0100 -Subject: [PATCH] [rest] Permit to externally provide cJSON library - -Permit to externally provide cJSON library if found with PKGConfig. - -Signed-off-by: Christian Marangi ---- - src/rest/CMakeLists.txt | 12 +++++++++++- - third_party/CMakeLists.txt | 5 ++++- - 2 files changed, 15 insertions(+), 2 deletions(-) - ---- a/src/rest/CMakeLists.txt -+++ b/src/rest/CMakeLists.txt -@@ -36,11 +36,22 @@ add_library(otbr-rest - response.cpp - ) - -+if (CJSON_FOUND) -+ set(CJSON_LIB_TARGETS ${CJSON_LINK_LIBRARIES}) -+else() -+ set(CJSON_LIB_TARGETS cjson) -+endif() -+ -+target_include_directories(otbr-rest -+ PRIVATE -+ ${CJSON_INCLUDE_DIRS} -+) -+ - target_link_libraries(otbr-rest - PUBLIC - http_parser - PRIVATE -- cjson -+ ${CJSON_LIB_TARGETS} - otbr-config - otbr-utils - openthread-ftd ---- a/third_party/CMakeLists.txt -+++ b/third_party/CMakeLists.txt -@@ -28,6 +28,13 @@ - - add_subdirectory(openthread) - if(OTBR_REST) -- add_subdirectory(cJSON) -+ pkg_check_modules(CJSON libcjson) -+ if (CJSON_FOUND) -+ set(CJSON_FOUND ${CJSON_FOUND} PARENT_SCOPE) -+ set(CJSON_INCLUDE_DIRS ${CJSON_INCLUDE_DIRS} PARENT_SCOPE) -+ set(CJSON_LINK_LIBRARIES ${CJSON_LINK_LIBRARIES} PARENT_SCOPE) -+ else() -+ add_subdirectory(cJSON) -+ endif() - add_subdirectory(http-parser) - endif() diff --git a/net/openthread-br/patches/102-openwrt-fix-LuCI-icons.patch b/net/openthread-br/patches/102-openwrt-fix-LuCI-icons.patch deleted file mode 100644 index 701ae0f201fb4e..00000000000000 --- a/net/openthread-br/patches/102-openwrt-fix-LuCI-icons.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 4b09d856fd3d82bfb6707d889fb40f981945a6a2 Mon Sep 17 00:00:00 2001 -From: Stijn Tintel -Date: Wed, 7 Jan 2026 00:20:04 +0200 -Subject: [PATCH] [openwrt] fix LuCI icons (#3178) - -The PNG icons in LuCI have been replaced with SVG icons. - -Signed-off-by: Stijn Tintel ---- - src/openwrt/view/admin_thread/thread_overview.htm | 10 +++++----- - src/openwrt/view/admin_thread/thread_scan.htm | 12 ++++++------ - 2 files changed, 11 insertions(+), 11 deletions(-) - ---- a/src/openwrt/view/admin_thread/thread_overview.htm -+++ b/src/openwrt/view/admin_thread/thread_overview.htm -@@ -32,7 +32,7 @@ - -
-
-- " id="wpan0" /> <%=threadget("interfacename").InterfaceName%> -+ " id="wpan0" /> <%=threadget("interfacename").InterfaceName%> -
-
- <%:Generic MAC 802.15.4 Thread%>
-@@ -164,13 +164,13 @@ - var scale = percent_thread_signal(info); - - if (scale == 0) -- icon = "<%=resource%>/icons/signal-0.png"; -+ icon = "<%=resource%>/icons/signal-000.svg"; - else if (scale == 30) -- icon = "<%=resource%>/icons/signal-25-50.png"; -+ icon = "<%=resource%>/icons/signal-025-050.svg"; - else if (scale == 50) -- icon = "<%=resource%>/icons/signal-50-75.png"; -+ icon = "<%=resource%>/icons/signal-050-075.svg"; - else -- icon = "<%=resource%>/icons/signal-75-100.png"; -+ icon = "<%=resource%>/icons/signal-075-100.svg"; - - return icon; - } ---- a/src/openwrt/view/admin_thread/thread_scan.htm -+++ b/src/openwrt/view/admin_thread/thread_scan.htm -@@ -17,17 +17,17 @@ - end - - if info.NetworkName == nil then -- icon = resource .. "/icons/signal-none.png" -+ icon = resource .. "/icons/signal-none.svg" - elseif scale < 15 then -- icon = resource .. "/icons/signal-0.png" -+ icon = resource .. "/icons/signal-000.svg" - elseif scale < 35 then -- icon = resource .. "/icons/signal-0-25.png" -+ icon = resource .. "/icons/signal-000-025.svg" - elseif scale < 55 then -- icon = resource .. "/icons/signal-25-50.png" -+ icon = resource .. "/icons/signal-025-050.svg" - elseif scale < 75 then -- icon = resource .. "/icons/signal-50-75.png" -+ icon = resource .. "/icons/signal-050-075.svg" - else -- icon = resource .. "/icons/signal-75-100.png" -+ icon = resource .. "/icons/signal-075-100.svg" - end - - return icon diff --git a/net/openthread-br/test.sh b/net/openthread-br/test.sh new file mode 100644 index 00000000000000..5e2bf54dbf43bc --- /dev/null +++ b/net/openthread-br/test.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# +# Functional smoke tests for openthread-br. + +set -e + +case "$PKG_NAME" in +openthread-br) + # Exercises otbr-agent's option parser and its full runtime closure. + # --version prints and exits without touching the RCP or the network. + # The Makefile passes OTBR_VERSION=$(PKG_VERSION), so this is also + # the string the generic version check matches. + otbr-agent --version + + # Use -h rather than --version here: ot-ctl has no version option at + # this release (openthread/openthread#13424 adds one, but the openthread + # bundled here predates it), so -h is the option that exits inside the + # parser. The older hazard -- a segfault on any unrecognized long + # option, from a getopt_long() array missing its terminating entry -- + # is fixed in the openthread this release bundles + # (openthread/openthread#13423). + # A usage exit of 0 or 1 both mean the parser ran to completion; + # anything else (a crash reports 128+signal) is what this catches. + set +e + ot-ctl -h >/dev/null 2>&1 + rc=$? + set -e + case $rc in + 0|1) ;; + *) + echo "test.sh: ot-ctl -h exited with status $rc" >&2 + exit 1 + ;; + esac + ;; + +*) + echo "test.sh: unknown package '$PKG_NAME', refusing to silently pass" >&2 + echo "test.sh: update net/openthread-br/test.sh to cover this package" >&2 + exit 1 + ;; +esac From 00914935385c8d0b43f6afa723f14645b9eb3a3b Mon Sep 17 00:00:00 2001 From: Christian Glombek Date: Sat, 25 Jul 2026 16:47:53 +0200 Subject: [PATCH 2/4] openthread-br: make the REST API listen address configurable As of the 2026.07.0 update, otbr-agent's REST API defaults to listening on 127.0.0.1, so remote clients (e.g. Home Assistant on another host) can no longer reach it. Add rest_listen_address and rest_listen_port proto options and pass them through to otbr-agent when set; leaving them unset keeps the secure loopback-only default. Also drop the leftover unused 'foobar' proto config option. Signed-off-by: Christian Glombek Assisted-By: AI (cherry picked from commit caacf174a8ad6c422aba2e91d1c20f53ffb61619) --- net/openthread-br/README.md | 14 +++++++++++--- net/openthread-br/files/openthread-proto.sh | 12 ++++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/net/openthread-br/README.md b/net/openthread-br/README.md index 99aea2459b2660..c06bfd0b565f30 100644 --- a/net/openthread-br/README.md +++ b/net/openthread-br/README.md @@ -70,6 +70,10 @@ D-Bus support. ### REST Server The REST server is enabled to make this package compatible with Home Assistant. +It listens on 127.0.0.1 by default. `rest_listen_address` and +`rest_listen_port` can move it, but the API is unauthenticated and can read and +replace the Thread dataset — including the network key — so any non-loopback +address must be firewalled to trusted hosts. ### TREL support @@ -112,9 +116,13 @@ config interface 'thread' option verbose '0' ``` -Prefix and verbose are optional. Everything else is required. The protocol -handler will fail if a required setting is missing. If something isn't working, -check ifstatus for the OpenThread interface: +Only backbone_network, device and radio_url are required; the protocol handler +fails the interface if one of them is missing, or if backbone_network names an +interface that has no device. Everything else — dataset, prefix, verbose, +rest_listen_address and rest_listen_port — is optional. See +[REST Server](#rest-server) before moving the REST API off the loopback +default. If something isn't working, check ifstatus for the OpenThread +interface: ``` # ifup thread diff --git a/net/openthread-br/files/openthread-proto.sh b/net/openthread-br/files/openthread-proto.sh index 995906c9d39429..7244820f562c88 100644 --- a/net/openthread-br/files/openthread-proto.sh +++ b/net/openthread-br/files/openthread-proto.sh @@ -39,7 +39,8 @@ proto_openthread_init_config() { proto_config_add_string backbone_network proto_config_add_string dataset proto_config_add_string radio_url - proto_config_add_string foobar + proto_config_add_string rest_listen_address + proto_config_add_int rest_listen_port available=1 no_device=1 @@ -61,7 +62,7 @@ proto_openthread_setup() { mkdir -p /var/lib/thread - json_get_vars backbone_network dataset device radio_url verbose:0 + json_get_vars backbone_network dataset device radio_url rest_listen_address rest_listen_port verbose:0 [ -n "$backbone_network" ] || proto_openthread_setup_error "$interface" MISSING_BACKBONE_NETWORK proto_add_host_dependency "$interface" "" "$backbone_network" @@ -78,6 +79,13 @@ proto_openthread_setup() { [ "$verbose" -eq 0 ] || append opts -v append opts "-I$device" append opts "-B$backbone_ifname" + # The REST API listens on 127.0.0.1 by default. Bind it elsewhere (e.g. a + # LAN address) to let remote clients such as Home Assistant reach it; + # leaving it unset keeps the loopback-only default. The REST API is + # unauthenticated and can both read and replace the Thread dataset, so any + # non-loopback address must be firewalled to trusted hosts. + [ -n "$rest_listen_address" ] && append opts "--rest-listen-address=$rest_listen_address" + [ -n "$rest_listen_port" ] && append opts "--rest-listen-port=$rest_listen_port" append opts "$radio_url" append opts "trel://$backbone_ifname" # run in subshell to prevent wiping json data needed for prefixes From 386519d43239441ff02f0b33f63859699f8dcc5f Mon Sep 17 00:00:00 2001 From: Christian Glombek Date: Sat, 25 Jul 2026 21:42:09 +0200 Subject: [PATCH 3/4] openthread-br: use OpenThread's internal mDNS Switch OTBR_MDNS from mDNSResponder to openthread, drop the mdnsd and mDNSResponder dependencies, and remove the proto handler's mdnsd service check, which would otherwise refuse to start otbr-agent now that mdnsd is no longer installed. This follows upstream, which made the internal implementation the default in openthread/ot-br-posix#2996 and uses it in its own OpenWrt example package, and it lets otbr-agent share a router with umdns, which is what advertises the router's procd-registered services (ssh, samba and the like) and resolves .local. The two can coexist because they never contend for a name: OpenThread's mDNS names its host after the Thread extended address, while umdns keeps .local. Both bind UDP 5353 with SO_REUSEADDR and each answers only for its own records. mDNSResponder instead takes its name from gethostname(), so it and umdns race for the same name, and the loser either stops announcing without logging anywhere procd captures or, if neither notices, both keep claiming it. That is the conflict with a running mdnsd noted in openwrt/packages#29784. Border router discovery is unaffected: in this mode OpenThread's own border agent registers the _meshcop._udp service (its OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE follows OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE), and OTBR's publisher is disabled by design, so clients such as Home Assistant still find the border router. Note that OpenThread's mDNS serves a single interface, the one given by the backbone_network option, so Thread service records are announced on that network only. Signed-off-by: Christian Glombek Assisted-By: AI (cherry picked from commit 233cfaa72c70cbcc82f35df3bcc090a586d5ff4e) --- net/openthread-br/Makefile | 6 ++--- net/openthread-br/README.md | 29 ++++++++++++++++----- net/openthread-br/files/openthread-proto.sh | 15 ----------- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/net/openthread-br/Makefile b/net/openthread-br/Makefile index ff1459a34ffb34..4758f411c691fe 100644 --- a/net/openthread-br/Makefile +++ b/net/openthread-br/Makefile @@ -33,9 +33,7 @@ define Package/openthread-br +libreadline \ +libstdcpp \ +libubox \ - +libubus \ - +mdnsd \ - +mdnsresponder + +libubus endef define Package/openthread-br/description @@ -55,7 +53,7 @@ CMAKE_OPTIONS += \ -DOT_TARGET_OPENWRT=ON \ -DOTBR_BACKBONE_ROUTER=ON \ -DOTBR_BORDER_ROUTING=ON \ - -DOTBR_MDNS=mDNSResponder \ + -DOTBR_MDNS=openthread \ -DOTBR_NAT64=OFF \ -DOTBR_OPENWRT=ON \ -DOTBR_PRODUCT_NAME=BorderRouter \ diff --git a/net/openthread-br/README.md b/net/openthread-br/README.md index c06bfd0b565f30..ecff952e5946ef 100644 --- a/net/openthread-br/README.md +++ b/net/openthread-br/README.md @@ -59,13 +59,28 @@ Therefore, firewall support is disabled completely. This can be revised once the following feature request is implemented: https://github.com/openthread/ot-br-posix/issues/1675 -### mDNSResponder - -The package depends on mDNSResponder. The alternative, Avahi, depends on D-Bus, -which is not something I feel comfortable with running on any router. While -there are Avahi packages without D-Bus support, using OpenThread Border Router -with Avahi requires libavahi-client, and this requires Avahi to be built with -D-Bus support. +### mDNS + +The package uses OpenThread's internal mDNS implementation +(`-DOTBR_MDNS=openthread`), which is upstream's default. This drops the +mDNSResponder dependency entirely: no separate daemon, and no Avahi, whose +libavahi-client requirement would have pulled in D-Bus. + +The internal implementation advertises on a single infrastructure interface, +the one selected by the `backbone_network` option. Anything that needs to be +announced on more than one interface still needs a general-purpose responder. + +It coexists with umdns, which remains the provider for other packages' +services. Both bind the wildcard address on port 5353 with SO_REUSEADDR, which +is what admits the second bind and gets multicast delivered to both, and they +never contend for a name: OpenThread's mDNS names its host after the Thread +extended address, while umdns keeps `.local`. + +Only multicast reaches both. A unicast datagram to port 5353 is delivered to +one socket, so a unicast reply meant for one daemon can be received by the +other. umdns does set SO_REUSEPORT, but only on a retry after its own bind +fails, and that does not happen here because SO_REUSEADDR already admits the +bind, so no SO_REUSEPORT group forms in either start order. ### REST Server diff --git a/net/openthread-br/files/openthread-proto.sh b/net/openthread-br/files/openthread-proto.sh index 7244820f562c88..fde143c51b5834 100644 --- a/net/openthread-br/files/openthread-proto.sh +++ b/net/openthread-br/files/openthread-proto.sh @@ -21,18 +21,6 @@ proto_openthread_add_prefix() { [ -n "$prefix" ] && $OTCTL prefix add $prefix } -proto_openthread_check_service() { - service="$1" - ret=1 - json_init - json_add_string name "$service" - ubus call service list "$(json_dump)" | jsonfilter -e '@[*].instances[*]["running"]' > /dev/null - ret=$? - json_cleanup - - return "$ret" -} - proto_openthread_init_config() { proto_config_add_array 'prefix:list(string)' proto_config_add_boolean verbose @@ -72,9 +60,6 @@ proto_openthread_setup() { [ -n "$device" ] || proto_openthread_setup_error "$interface" MISSING_DEVICE [ -n "$radio_url" ] || proto_openthread_setup_error "$interface" MISSING_RADIO_URL - # run in subshell to prevent wiping json data needed for prefixes - ( proto_openthread_check_service mdnsd ) || proto_openthread_setup_error "$interface" MISSING_SVC_MDNSD - opts="--auto-attach=0" [ "$verbose" -eq 0 ] || append opts -v append opts "-I$device" From 5b1596522d0202a18a74ec4c5c31bd01d1eb2cb2 Mon Sep 17 00:00:00 2001 From: Christian Glombek Date: Wed, 2 Sep 2026 07:04:06 +0200 Subject: [PATCH 4/4] openthread-br: use the in-process nftables firewall backend The package has never had a Thread firewall: the OpenThread posix platform's own one (OT_FIREWALL) produces ipset/ip6tables rules, so it is disabled here, and the README documents waiting for openthread/ot-br-posix#1675 to revisit that. v2026.09.0 delivers what that issue asked for: an in-process nftables backend. Build with OTBR_NFTABLES=ON: otbr-agent then installs the Thread ingress filter and the NAT44 masquerade itself, through nftables in an isolated table, matching what an fw4-based OpenWrt runs. This needs nf_tables kernel support at runtime, so add kmod-nft-core and kmod-nft-nat alongside the userspace libmnl/libnftnl: with the backend compiled in, a failed firewall install aborts the agent rather than silently forwarding unfiltered traffic. OT_FIREWALL deliberately stays off; the in-process backend replaces its ipset producer wholesale. Also ship the marker file the build writes. Nothing on the target reads it today - it records which backend owns the rules, as a debugging aid and for anything that later needs to tell the backends apart. Signed-off-by: Christian Glombek Assisted-By: AI --- net/openthread-br/Makefile | 10 +++++++++- net/openthread-br/README.md | 26 +++++++++++++++++++------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/net/openthread-br/Makefile b/net/openthread-br/Makefile index 4758f411c691fe..aaad7c5409efbb 100644 --- a/net/openthread-br/Makefile +++ b/net/openthread-br/Makefile @@ -24,12 +24,16 @@ define Package/openthread-br SECTION:=net TITLE:=OpenThread Border Router DEPENDS:= \ + +kmod-nft-core \ + +kmod-nft-nat \ +kmod-tun \ +libblobmsg-json \ +libjson-c \ +cJSON \ + +libmnl \ +libncurses \ +libnetfilter-queue \ + +libnftnl \ +libreadline \ +libstdcpp \ +libubox \ @@ -55,6 +59,7 @@ CMAKE_OPTIONS += \ -DOTBR_BORDER_ROUTING=ON \ -DOTBR_MDNS=openthread \ -DOTBR_NAT64=OFF \ + -DOTBR_NFTABLES=ON \ -DOTBR_OPENWRT=ON \ -DOTBR_PRODUCT_NAME=BorderRouter \ -DOTBR_REST=ON \ @@ -69,9 +74,12 @@ define Package/openthread-br/install $(INSTALL_DIR) \ $(1)/etc/init.d \ $(1)/lib/netifd/proto \ - $(1)/usr/sbin + $(1)/usr/sbin \ + $(1)/usr/share/otbr $(INSTALL_BIN) ./files/openthread-proto.sh $(1)/lib/netifd/proto/openthread.sh $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/* $(1)/usr/sbin + $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/otbr/nftables-backend \ + $(1)/usr/share/otbr endef diff --git a/net/openthread-br/README.md b/net/openthread-br/README.md index ecff952e5946ef..4d26602a68ee11 100644 --- a/net/openthread-br/README.md +++ b/net/openthread-br/README.md @@ -51,13 +51,25 @@ option would make the package fail it again. ### Firewall support -OpenWrt uses firewall4 with nftables by default, but the OpenThread firewall -implementation uses IPTables and IPset. While we still support firewall3 with -IPTables, it's not a good idea to add new dependencies to old things. -Therefore, firewall support is disabled completely. - -This can be revised once the following feature request is implemented: -https://github.com/openthread/ot-br-posix/issues/1675 +OpenWrt uses firewall4 with nftables. The OpenThread posix platform's own +firewall (`OT_FIREWALL`) produces ipset/ip6tables rules, so it has always been +disabled here and no Thread ingress filter existed at all. + +Since v2026.09.0 upstream provides exactly what +https://github.com/openthread/ot-br-posix/issues/1675 asked for: an in-process +nftables backend (`OTBR_NFTABLES`). It is enabled, so otbr-agent now installs +the Thread ingress filter and the NAT44 masquerade itself, through nftables in +an isolated `otbr` table, matching what the rest of the system uses. This +needs nf_tables kernel support at runtime, hence the `kmod-nft-core` and +`kmod-nft-nat` dependencies. `OT_FIREWALL` deliberately stays off: the +in-process backend replaces its ipset producer wholesale, and upstream's CI +builds the same pairing. + +The build also writes a `/usr/share/otbr/nftables-backend` marker recording +which backend owns the rules. Nothing on an OpenWrt target reads it today (its +consumers are upstream's setup scripts, which this package does not install); +it is shipped as a debugging aid and for anything that later needs to tell the +backends apart. ### mDNS