Getting Started: Wi-Fi PAF for the Commissioning Proxy#
This guide is a self-contained walkthrough for getting the Commissioning Proxy
(CP) device in all-devices-app working over Wi-Fi PAF (Wi-Fi Aware / NAN
— Neighbor Awareness Networking).
The Commissioning Proxy tunnels commissioning traffic between a Commissioner (chip-tool) and a Commissionee that the Commissioner cannot reach directly:
chip-tool ──Matter (TCP/IP)──► all-devices-app (CP) ──Wi-Fi PAF (NAN)──► Commissionee
Wi-Fi PAF is one of the transports the CP device can be built with (BLE is
another). Both are compiled in by default on Linux; the transport used to reach
a commissionee is chosen per command by the Transport field of the
request, not by a command-line flag. This guide covers Wi-Fi PAF end to end —
everything you need to do to make PAF work, from hardware to a completed
commission-through-proxy.
1. Hardware prerequisites#
Wi-Fi PAF needs a radio and driver that support NAN USD (Wi-Fi Aware Unsynchronized Service Discovery). Both the proxy and the commissionee need it.
Role |
Requirement |
|---|---|
Commissioning Proxy (DUT) |
Linux host (e.g. Raspberry Pi 4/5) with a USB Wi-Fi dongle that supports NAN USD. See https://groups.csa-iot.org/wg/members-all/document/44361 |
End Device (commissionee) |
Same NAN-USD-capable USB Wi-Fi dongle requirement |
The on-board Raspberry Pi Wi-Fi does not support NAN USD and cannot be used for Wi-Fi PAF. The USB dongle must be the interface wpa_supplicant manages.
On each device with a PAF dongle, install the wireless utilities. Without them
the proxy fails at startup with
[PAF] Failed to start Wi-Fi PAF publish: ... CHIP Error 0x00000003: Incorrect state:
sudo apt install net-tools wireless-tools
2. wpa_supplicant with the Matter NAN patch (proxy device)#
The Commissioning Proxy requires a custom build of wpa_supplicant. The stock Ubuntu package does not compile in NAN USD support, and its default NAN scan behavior interferes with the proxy’s background scan.
2.1 Why the stock package is not enough#
The stock
wpa_supplicantis built withCONFIG_NAN_USDdisabled, so it has no Wi-Fi Aware support at all.When the proxy performs a NAN scan it subscribes to the Matter NAN service as a passive listener. By default wpa_supplicant automatically sends a NAN Follow-up frame back to every matching publisher, which triggers spurious session setup. The patch adds a
discovery_onlyflag that suppresses those automatic replies for discovery-only scans.
2.2 The patch#
The patch touches four files:
File |
Change |
|---|---|
|
Enables |
|
Adds a |
|
Skips the automatic Follow-up reply to the publisher when |
|
Exposes |
Save the following as wpa-supplicant-matter.patch:
diff --git a/src/common/nan_de.c b/src/common/nan_de.c
index 2af1afd73..1196ec617 100644
--- a/src/common/nan_de.c
+++ b/src/common/nan_de.c
@@ -910,7 +910,8 @@ static void nan_de_rx_publish(struct nan_de *de, struct nan_de_service *srv,
nan_de_tx_multicast(de, srv, instance_id);
}
- if (!de->offload && !srv->subscribe.active && req_instance_id == 0) {
+ if (!de->offload && !srv->subscribe.active && req_instance_id == 0 &&
+ !srv->subscribe.discovery_only) {
/* Passive subscriber replies with a Follow-up message without
* Service Specific Info field if it received a matching
* unsolicited Publish message. */
diff --git a/src/common/nan_de.h b/src/common/nan_de.h
index b2688a83c..6b9e2ad1f 100644
--- a/src/common/nan_de.h
+++ b/src/common/nan_de.h
@@ -154,6 +154,11 @@ struct nan_subscribe_params {
/* Proximity ranging flag */
bool proximity_ranging;
+
+ /* If true, suppress the automatic Follow-up sent to the publisher when
+ * a passive subscriber matches an unsolicited Publish frame. Use for
+ * discovery-only scans where no session is to be established. */
+ bool discovery_only;
};
/* Returns -1 on failure or >0 subscribe_id */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 76a9297eb..8a9cec6a3 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -6855,6 +6855,10 @@ DBusMessage * wpas_dbus_handler_nan_subscribe(DBusMessage *message,
for (i = 0; i < entry.array_len; i++)
int_array_add_unique(
&freq_list, entry.uint16array_value[i]);
+ } else if (os_strcmp(entry.key, "discovery_only") == 0 &&
+ entry.type == DBUS_TYPE_BOOLEAN) {
+ params.discovery_only = entry.bool_value;
+ wpa_dbus_dict_entry_clear(&entry);
} else {
wpa_printf(MSG_DEBUG,
"dbus: NANSubscribe - unsupported dict entry '%s'",
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 84ac8ba12..2a8f3e9f3 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -681,4 +681,4 @@ CONFIG_DPP2=y
#CONFIG_NO_WMM_AC=y
# Wi-Fi Aware unsynchronized service discovery (NAN USD)
-#CONFIG_NAN_USD=y
+CONFIG_NAN_USD=y
2.3 Build and install#
Building natively on the proxy device is simplest:
# 1. Clone upstream hostap and check out the pinned commit
git clone https://w1.fi/hostap.git
cd hostap
git checkout e17107912
# 2. Apply the Matter NAN patch (adjust the path to where you saved it)
git apply /path/to/wpa-supplicant-matter.patch
# 3. Use the patched defconfig as the active build config
cd wpa_supplicant
cp defconfig .config
# 4. Install build dependencies
sudo apt update
sudo apt install libnl-3-dev libnl-genl-3-dev libnl-route-3-dev
# 5. Build
make all
Verify NAN USD was compiled in, then install and restart the service:
strings wpa_supplicant | grep -c nan_usd # should be > 0
sudo systemctl stop wpa_supplicant
sudo cp wpa_supplicant /usr/sbin/wpa_supplicant
sudo systemctl start wpa_supplicant
systemctl status wpa_supplicant # expect active (running)
3. Build all-devices-app with Wi-Fi PAF#
Wi-Fi PAF is gated by the GN arg chip_device_config_enable_wifipaf, which
defaults to true on Linux builds with Wi-Fi enabled (see
src/platform/device.gni). The Commissioning Proxy cluster itself is enabled by
CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONING_PROXY, which is forced on for this app
in posix/include/CHIPProjectAppConfig.h. So a normal Linux build already
includes Wi-Fi PAF — no extra args are required.
The PAF transport driver (CommissioningProxyPafTransport.cpp) is compiled only
when chip_device_config_enable_wifipaf is set; a build with it off drops the
WiFiNetworkInterface cluster feature and the WiFiBand attribute.
Upstream build (Linux x86-64 / ARM), both transports:
# x86-64
./scripts/run_in_build_env.sh \
"./scripts/build/build_examples.py --target linux-x64-all-devices-boringssl build"
# ARM (arm64)
./scripts/run_in_build_env.sh \
"./scripts/build/build_examples.py --target linux-arm64-all-devices-boringssl build"
The binary is at out/linux-x64-all-devices-boringssl/all-devices-app (or the
arm64 equivalent).
Select the transports at build time:
The targets above compile every transport the platform supports. To build a single-transport binary, configure the application’s own gn root and pass the flag that disables the other transport:
./scripts/run_in_build_env.sh "cd examples/all-devices-app/posix && \
gn gen out/paf-only --args='chip_config_network_layer_ble=false' && \
ninja -C out/paf-only all-devices-app"
gn argument |
Transports compiled in |
|---|---|
(none of the below) |
BLE and Wi-Fi PAF |
|
Wi-Fi PAF only |
|
BLE only |
The binary lands at out/<name>/all-devices-app under
examples/all-devices-app/posix. Cross-compiling for a Raspberry Pi adds the
usual aarch64 arguments to the same gn gen line — target_os="linux",
target_cpu="arm64", and a sysroot= and system_libdir= pointing at your
aarch64 sysroot.
4. Run the Commissioning Proxy#
Start the proxy on the device with the NAN-capable dongle. Select the CP device
and its endpoint with --device commissioning-proxy:5, and enable Wi-Fi
management. freq_list=2437 selects channel 6 (2.4 GHz) — the default Matter
PAF channel, permitted in all regulatory regions:
./all-devices-app \
--device commissioning-proxy:5 \
--wifi \
--wifipaf "freq_list=2437" \
--discriminator 3947
Argument |
Description |
|---|---|
|
Instantiate the CP device on endpoint 5 |
|
Enable Wi-Fi management via wpa_supplicant (required for PAF) |
|
NAN frequencies in MHz. |
|
12-bit value identifying the proxy during its own commissioning |
freq_list is parsed once at startup and drives three things. The WiFiBand
attribute advertised by the cluster follows the bands it covers: 2412–2484 MHz →
2.4 GHz, 5035–5980 MHz → 5 GHz, defaulting to 2.4 GHz if no valid frequency is
parsed. Scans and ProxyConnectRequest create a subscribe instance on a single
channel: 2437 when it is listed, otherwise the first frequency given. The
proxy’s own NAN publisher advertises on the whole list, so the proxy can be
commissioned over Wi-Fi PAF itself.
That publisher stops once the proxy joins a fabric, as the NAN radio is needed to subscribe on a Commissionee behalf.
Once the proxy is commissioned (next step), it disconnects the NAN receive handler, so the PAF subscribe calls it makes on behalf of commissioners register exactly one handler.
5. Commission the proxy onto the fabric#
The proxy must itself join the Matter fabric before it can tunnel for others. From the Commissioner run a one-time on-network pairing (the proxy is reachable over IP):
./chip-tool pairing onnetwork 1998 20202021
1998 is the node ID assigned to the proxy (any non-zero value); 20202021 is
the default PASE passcode. Success prints:
CHIP:TOO: Device commissioning completed with success
Tip: the proxy re-reads its stored fabric credential on restart. You do not need to re-pair unless you clear its key-value store (
/tmp/chip_*).
6. Run the End Device (commissionee)#
Start the commissionee (for example a lighting-app) with a freq_list that
matches — or overlaps — the proxy’s. The End Device must not already be on a
network, as it is about to be commissioned:
./chip-lighting-app \
--wifi \
--wifipaf "freq_list=2437" \
--discriminator 3840
The End Device advertises itself as a NAN publisher. Confirm in its log:
WiFi-PAF: Starting NAN publish
The End Device discriminator (
3840here) must match the value you pass tochip-tool pairing proxyin step 8.
7. Scanning (optional)#
Before connecting, a Commissioner can ask the proxy to scan for nearby
commissionable devices. In these commands 8 is the Wi-Fi PAF transport bit,
1998 is the proxy node ID (use whatever you paired in step 5), and 5 is the
endpoint where the cluster lives. The --WiFiBands bitmap is 1 = 2.4 GHz, 4
= 5 GHz, 5 = both.
Foreground (one-shot) scan — results are returned inline. This command carries a large payload, so TCP must be forced:
./chip-tool commissioningproxy proxy-scan-request 8 1998 5 \
--allow-large-payload true --WiFiBands 5 --timeout 20
Background scan — the proxy scans continuously and caches results, pushed to
subscribers via attribute reporting. Timeout 0 means no expiry:
# start
./chip-tool commissioningproxy proxy-back-ground-scan-start-request 8 0 1998 5 \
--WiFiBands 5
# read the cache
./chip-tool commissioningproxy read cached-results 1998 5
# stop
./chip-tool commissioningproxy proxy-back-ground-scan-stop-request 8 1998 5 \
--WiFiBands 5
8. Commission the End Device through the proxy#
Use chip-tool pairing proxy to commission the End Device through the proxy.
The proxy opens a Wi-Fi PAF (NAN) session to the End Device and tunnels the PASE
and commissioning packets between chip-tool and the device:
./chip-tool pairing proxy <node-id> <ssid> <password> <setup-pin-code> \
<ed-discriminator> <proxy-node-id> <proxy-connect-timeout> <proxy-transport> \
[--proxy-endpoint <endpoint>] [--proxy-wifi-band 2g4|5g]
Argument |
Description |
|---|---|
|
Node ID to assign to the new device |
|
Wi-Fi network SSID to configure on the device |
|
Wi-Fi network password |
|
Commissionee PASE setup PIN code |
|
Commissionee discriminator (matches step 6) |
|
Node ID of the already-commissioned proxy |
|
Seconds the proxy may spend connecting to the commissionee; |
|
|
|
Endpoint hosting the cluster. Defaults to |
|
Optional band hint, |
Example — commission device node 1999 onto network “MyNetwork” via proxy 1998:
./chip-tool pairing proxy 1999 "MyNetwork" "MyPassword" 20202021 3840 1998 0 wifipaf \
--proxy-endpoint 5 --proxy-wifi-band 2g4
9. How Wi-Fi PAF is wired into the CP device#
The Wi-Fi PAF driver, CommissioningProxyPafTransport, ships with the cluster
as the separate paf-transport target, so a build that proxies over BLE only —
or a platform with no Wi-Fi PAF stack — can leave it out. Its design, the
transport methods it implements and the shared-scanner rules are documented in
src/app/clusters/commissioning-proxy-server/README.md.
What this app has to supply#
Session setup and message tunneling are portable. Discovery is not: scanning for
commissionable devices, and recovering the NAN subscribe id the platform
assigned, both need platform code. That is the CommissioningProxyPafAdapter
interface, which this app implements in
posix/linux/CommissioningProxyPafAdapter.cpp:
Adapter method |
Linux implementation |
|---|---|
|
|
|
|
|
|
|
|
The adapter is also where the platform’s peer descriptor is unpacked.
NanPeerInfo is Linux-only and owns heap storage for its extended data, so the
adapter reports each peer as plain scalars and that type never reaches the
cluster.
Adapter and driver are both constructed in
posix/linux/DeviceFactoryPlatformOverride.cpp, which composes the driver onto
the single CommissioningProxyDevice with AddTransport() — a build with BLE
adds that driver the same way — and derives the advertised WiFiBand from
--wifipaf freq_list=, since the device itself reads no command line. The same
<<<<<<< HEAD parsed list reaches the radio from posix/main.cpp. ======= parsed
list reaches the radio from posix/main.cpp. Transports are registered before
Server::Init(), so the fabric table is empty at that point and the driver’s
DisconnectPublishReceiveHandler() call lands on the commissioning-complete
event instead.
10. Troubleshooting#
Proxy fails to start with a NAN error#
Confirm the USB Wi-Fi dongle is present (
lsusb) and its interface exists (ip link).Confirm the patched wpa_supplicant is installed and running:
systemctl status wpa_supplicantshowsactive (running), andstrings /usr/sbin/wpa_supplicant | grep -c nan_usdreturns > 0.Do not use the on-board RPi Wi-Fi — it does not support NAN USD.
Ensure
net-toolsandwireless-toolsare installed (see section 1); a missingiwconfigsurfaces asCHIP Error 0x00000003: Incorrect state.
Proxy connect times out (Commissioner gets Status::Timeout)#
The proxy waited for the connect timeout on the NAN session and gave up. Common causes:
The End Device is not running or is on a different NAN channel — confirm both sides use the same
freq_list.The End Device host also needs a NAN-capable USB Wi-Fi dongle.
The NAN frequency is not allowed by the regulatory domain.
freq_list=2437(channel 6, 2.4 GHz) is permitted everywhere.
PAF session closes mid-commissioning#
Look for WiFiPAFCloseSession in the proxy log — the PAFTP ack-receive timer
fired. The most common cause is the End Device’s radio going off-channel while
it scans for and joins the Wi-Fi network. Retrying usually succeeds.
ConnectNetwork is slow#
ConnectNetwork can take 10–15 s while the End Device scans for and joins the
Wi-Fi AP. This is normal; the proxy extends the Commissioner’s exchange timeout
for this step.
chip-tool pairing proxy command not found#
You are running a chip-tool build without the Commissioning Proxy support. Build chip-tool from this repository.