Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions service/matter-netman/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ PKG_SOURCE_SUBMODULES:=\
third_party/nlio/repo

# Hash can be regenerated with make package/matter-netman/check FIXUP=1
PKG_SOURCE_DATE:=2026-07-30
PKG_SOURCE_VERSION:=72ecdc2bca57e99b3defea68eb3bcc5bd8bebdbd
PKG_MIRROR_HASH:=6812910e012294a9b812bc7550e352e981627e8bec932d3634e098be68ebe7fb
PKG_SOURCE_DATE:=2026-08-21
PKG_SOURCE_VERSION:=b791201722360efc16b5a4fe152123f67f2acc2b
PKG_MIRROR_HASH:=0b1235d884b13a3e8b0bd4e20920785dd31e9b9a6d71e1206167dfc869dc05d0

# Use local source dir for development
# USE_SOURCE_DIR:=$(HOME)/workspace/connectedhomeip
Expand Down Expand Up @@ -107,7 +107,10 @@ $(Package/matter-netman/default/description)
This variant of the package uses the OpenSSL crypto library.
endef

# General options
# General options. The last two deliberately reverse what the example's
# args.gni asks for: access restrictions are backed only by the SDK's
# demonstration provider, which lifts every restriction on request, and
# tracing is of no use in a shipped build.
CONFIGURE_OPTIONS:=\
--enable-ubus \
--enable-detail-logging \
Expand Down Expand Up @@ -140,12 +143,16 @@ TARGET_CFLAGS+=\
# Device Information. Runtime information is configured by bootstrap.sh
include $(INCLUDE_DIR)/version.mk
OS_VERSION:=$(if $(filter-out SNAPSHOT,$(VERSION_NUMBER)),$(VERSION_NUMBER)-)$(VERSION_CODE)
# The identity strings are gn arguments rather than -D in TARGET_CFLAGS: the
# configure script splits CFLAGS on whitespace, so a define whose value has a
# space in it does not survive. The serial number stays a define because its
# gn argument emits nothing for the empty string, which is the value wanted.
CONFIGURE_OPTIONS+=\
--device-config-device-software-version-string="$(PKG_VERSION)@$(OS_VERSION)" \
--device-config-device-hardware-version-string="-" \
--device-config-device-vendor-name="$(VERSION_DIST)"
TARGET_CFLAGS+=\
-DCHIP_DEVICE_CONFIG_ENABLE_TEST_SETUP_PARAMS=0 \
-DCHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING=\"$(PKG_VERSION)@$(OS_VERSION)\" \
-DCHIP_DEVICE_CONFIG_DEFAULT_DEVICE_HARDWARE_VERSION_STRING=\"-\" \
-DCHIP_DEVICE_CONFIG_DEVICE_VENDOR_NAME=\"$(VERSION_DIST)\" \
-DCHIP_DEVICE_CONFIG_DEVICE_PRODUCT_NAME=\"\" \
-DCHIP_DEVICE_CONFIG_TEST_SERIAL_NUMBER=\"\"

# https://github.com/openwrt/openwrt/issues/13016
Expand All @@ -169,18 +176,20 @@ define Build/Compile
endef

define Package/matter-netman/default/install
$(INSTALL_DIR) $(1)/usr/sbin $(1)/usr/share/matter $(1)/etc/init.d $(1)/usr/share/acl.d
$(INSTALL_DIR) $(1)/usr/sbin $(1)/usr/share/matter $(1)/etc/init.d $(1)/usr/share/acl.d $(1)/etc/config
$(INSTALL_BIN) $(OUT_DIR)/matter-network-manager-app $(1)/usr/sbin
$(INSTALL_DATA) ./files/bootstrap.sh $(1)/usr/share/matter/
$(INSTALL_DATA) ./files/matter_acl.json $(1)/usr/share/acl.d
$(INSTALL_BIN) ./files/matter.init $(1)/etc/init.d/matter
$(INSTALL_CONF) ./files/matter.config $(1)/etc/config/matter
Comment thread
LorbusChris marked this conversation as resolved.
endef

Package/matter-netman-mbedtls/install=$(Package/matter-netman/default/install)
Package/matter-netman-openssl/install=$(Package/matter-netman/default/install)

define Package/matter-netman/default/conffiles
$(CONF_DIR)/
/etc/config/matter
endef

Package/matter-netman-mbedtls/conffiles=$(Package/matter-netman/default/conffiles)
Expand Down
26 changes: 26 additions & 0 deletions service/matter-netman/files/matter.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
config matter 'settings'
# Share the LAN access point credentials over the Matter Wi-Fi
# Network Management cluster. Set to 0 to share nothing.
option wifi_share '1'
# The netifd network whose access point credentials are shared.
option wifi_network 'lan'
# Pin a specific wifi-iface section instead of the automatic choice.
# option wifi_iface 'default_radio0'
# The interface this device is reachable on. It is reported first in
# the network diagnostics, so a controller asking which interface the
# device uses gets this one, and it names the network the Ethernet
# diagnostics describe.
# option primary_interface 'br-lan'
# The manufacturer reported in Basic Information. Unset, the one the
# firmware states in /etc/os-release is used.
# option vendor_name 'CZ.NIC'
# The product reported in Basic Information. Unset, the distribution
# name from /etc/os-release is used, which is how a controller names
# the firmware rather than the board it runs on.
# option product_name 'Turris OS'
# Interface state and traffic counters are readable by every paired
# controller. Set to 0 to report none of them.
# option ethernet_diagnostics '1'
# Take the Ethernet diagnostics from this interface instead of the
# primary one, to report the state of a port rather than a bridge.
# option diagnostics_interface 'eth1'
100 changes: 98 additions & 2 deletions service/matter-netman/files/matter.init
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,105 @@ start_service() {
. /usr/share/matter/bootstrap.sh

procd_open_instance
procd_set_param command /bin/sh -c 'umask 027; exec "$@"' - "$PROG"
procd_set_param command /bin/sh -c 'umask 027; exec "$@"' - "$PROG" $(matter_args)
local vendor_name product_name
config_load matter
config_get vendor_name settings vendor_name ""
config_get product_name settings product_name ""
# Appended separately so a multi-word name stays one argument.
[ -n "$vendor_name" ] && procd_append_param command --vendor-name "$vendor_name"
[ -n "$product_name" ] && procd_append_param command --product-name "$product_name"
procd_set_param user matter
procd_set_param group matter
procd_set_param respawn
# Retry indefinitely: a start that loses a port race with a dying
# predecessor must not strand the service in procd's crash-loop state.
procd_set_param respawn 3600 5 0
procd_close_instance
}

restart() {
# procd's stop is asynchronous, and the daemon needs a moment to tear
# the Matter server down; starting over it loses the port race and
# dies with "Address in use". Wait for the old instance to be gone.
stop "$@"
local i=0
while pidof matter-network-manager-app >/dev/null && [ "$i" -lt 30 ]; do
sleep 1
i=$((i + 1))
done
start "$@"
}

service_triggers() {
procd_add_reload_trigger "matter" "wireless" "network"
}

wifi_args() {
local wifi_share wifi_network wifi_iface

config_load matter
config_get_bool wifi_share settings wifi_share 1
config_get wifi_network settings wifi_network "lan"
config_get wifi_iface settings wifi_iface ""
if [ "$wifi_share" = 0 ]; then
echo -n "--no-wifi-share"
return
fi

echo -n "--wifi-network $wifi_network"
[ -n "$wifi_iface" ] && echo -n " --wifi-iface $wifi_iface"
}

interface_args() {
local primary diag eth_diag

config_load matter
config_get primary settings primary_interface "br-lan"
config_get diag settings diagnostics_interface ""
config_get_bool eth_diag settings ethernet_diagnostics 1
echo -n "--primary-interface $primary"
[ -z "$diag" ] || echo -n " --diagnostics-interface $diag"
[ "$eth_diag" = 1 ] || echo -n " --no-ethernet-diagnostics"
}

thread_args() {
# Without the border router there is no Thread network to manage.
[ -x /usr/sbin/otbr-agent ] || echo -n "--no-thread"
}

matter_args() {
local args thread

args="$(wifi_args) $(interface_args)"
thread="$(thread_args)"
[ -n "$thread" ] && args="$args $thread"
echo -n "$args"
}

reload_service() {
local pid running

# Which access point to share is a command line argument, so a changed
# configuration needs a restart; a changed wireless configuration only
# needs the daemon to re-read the credentials of the same access point,
# which keeps the Matter sessions up.
pid="$(pidof matter-network-manager-app)"
# First PID only, and never read /proc//cmdline (the kernel command
# line) when the daemon is not running at all.
pid="${pid%% *}"
running=""
[ -n "$pid" ] && running="$(tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null)"
local vendor_name product_name
config_load matter
config_get vendor_name settings vendor_name ""
config_get product_name settings product_name ""
# Compare the whole command line, not a substring: an option that was
# removed leaves the expected arguments a prefix of the running ones,
# which must count as a change. echo collapses the whitespace.
# shellcheck disable=SC2086 # word splitting collapses the whitespace
if [ "$(set -f; echo $running)" = "$(set -f; echo "$PROG" $(matter_args)${vendor_name:+ --vendor-name $vendor_name}${product_name:+ --product-name $product_name})" ]; then
ubus call matter reload_wifi
else
restart
fi
Comment thread
LorbusChris marked this conversation as resolved.
}
20 changes: 17 additions & 3 deletions service/matter-netman/files/matter_acl.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,23 @@
"user": "matter",
"access": {
"otbr": {
"methods": ["*"]
"methods": [
"*"
]
},
"network.wireless": {
"methods": [
"status"
]
}
},
"subscribe": [ "otbr" ],
"listen": [ "ubus.object.*" ]
"subscribe": [
"otbr"
],
"listen": [
"ubus.object.*"
],
"publish": [
"matter"
]
}
69 changes: 0 additions & 69 deletions service/matter-netman/patches/010-zap-disable-arl.patch

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
From 8eb63023e94bffaacfe608c97db0c141b8705196 Mon Sep 17 00:00:00 2001
From: Christian Glombek <c.glombek@cosa.systems>
Date: Mon, 27 Jul 2026 07:34:19 +0200
Subject: [PATCH] [network-manager] implement RevertActiveDataset in the fake
delegate

The fake border router returned NOT_IMPLEMENTED, so fail-safe rollback
could not be exercised without real hardware.

SetActiveDataset is only accepted when no dataset is configured, so
reverting means returning to the unconfigured state rather than restoring
a previous dataset. Clear it and report the timestamp change.

Assisted-By: Claude Opus 5
---
.../network-manager-app/linux/ThreadBRFake.h | 41 +++++++++++++++++--
1 file changed, 37 insertions(+), 4 deletions(-)

diff --git a/examples/network-manager-app/linux/ThreadBRFake.h b/examples/network-manager-app/linux/ThreadBRFake.h
index 86a92c7c54..453c78ebeb 100644
--- a/examples/network-manager-app/linux/ThreadBRFake.h
+++ b/examples/network-manager-app/linux/ThreadBRFake.h
@@ -87,12 +87,44 @@ class FakeBorderRouterDelegate final : public app::Clusters::ThreadBorderRouterM

mActivateDatasetCallback = callback;
mActivateDatasetSequence = sequenceNum;
- TEMPORARY_RETURN_IGNORED DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(1000), ActivateActiveDataset,
- this);
+ mActivationPending = true;
+ VerifyOrReturn(DeviceLayer::SystemLayer()
+ .StartTimer(System::Clock::Milliseconds32(1000), ActivateActiveDataset, this)
+ .Handle([&](CHIP_ERROR error) {
+ // Without the timer nothing would ever complete this activation;
+ // undo the state so the next attempt is not refused as Busy.
+ mActivateDatasetCallback = nullptr;
+ mActivationPending = false;
+ mActiveDataset.Clear();
+ callback->OnActivateDatasetComplete(sequenceNum, error);
+ }));
}

- CHIP_ERROR CommitActiveDataset() override { return CHIP_NO_ERROR; }
- CHIP_ERROR RevertActiveDataset() override { return CHIP_ERROR_NOT_IMPLEMENTED; }
+ CHIP_ERROR CommitActiveDataset() override
+ {
+ mActivationPending = false;
+ return CHIP_NO_ERROR;
+ }
+
+ CHIP_ERROR RevertActiveDataset() override
+ {
+ // Parity with the ubus delegate: the fail-safe handler calls this for
+ // every expiry, and only an activation that was not committed reverts.
+ VerifyOrReturnError(mActivationPending, CHIP_NO_ERROR);
+ mActivationPending = false;
+
+ // The activation timer may still be armed. A reverted activation must
+ // neither report success nor block the next attempt.
+ DeviceLayer::SystemLayer().CancelTimer(ActivateActiveDataset, this);
+ mActivateDatasetCallback = nullptr;
+
+ // SetActiveDataset is only accepted when no dataset is configured, so
+ // reverting it means returning to the unconfigured state.
+ mActiveDataset.Clear();
+ mAttributeChangeCallback->ReportAttributeChanged(
+ app::Clusters::ThreadBorderRouterManagement::Attributes::ActiveDatasetTimestamp::Id);
+ return CHIP_NO_ERROR;
+ }

CHIP_ERROR SetPendingDataset(const Thread::OperationalDataset & pendingDataset) override
{
@@ -130,6 +162,7 @@ private:

ActivateDatasetCallback * mActivateDatasetCallback = nullptr;
uint32_t mActivateDatasetSequence;
+ bool mActivationPending = false;
};

} // namespace chip
Loading