ast STA roaming (802.11r FT) between two HaLow APs — handoff fails: "lands then deauths" within 3s

Hello Morse Micro Support,

We are seeing slow STA->STA handoffs (~2 seconds) on MM8108-EKH01 and would like to confirm whether 802.11r FT is supported on the current OpenWrt/driver stack, and if so the correct configuration to achieve sub-200 ms cross-channel STA roams.

Setup

  • Hardware: 2× MM8108-EKH01 boards configured as HaLow APs + 1× MM8108-EKH01 board as HaLow STA, all on Raspberry Pi 4

  • Software (from STA g7-sta-00000001):

$ uname -r
5.15.167
$ cat /etc/openwrt_release
DISTRIB_ID=‘OpenWrt’
DISTRIB_RELEASE=‘23.05.5’
DISTRIB_REVISION=‘r0+24325-747fb9b5e1’
DISTRIB_TARGET=‘bcm27xx/bcm2711’
DISTRIB_ARCH=‘aarch64_cortex-a72’
DISTRIB_DESCRIPTION=‘OpenWrt 23.05.5 Morse-2.9.3’
$ wpa_cli_s1g -v
wpa_cli v2.12-morse_micro-rel_1_16_4_2025_Sep_18
$ hostapd_s1g -v
hostapd v2.12-morse_micro-rel_1_16_4_2025_Sep_18

  • Driver: morse_driver from the 2.9.3 release tarball (/usr/sbin daemons dated 2025-Sep-18).

  • Security: WPA3-Personal (SAE), same SSID (g7-AP), same PSK on both APs

  • Channels: AP1 on CH_28 (S1G ch 28, mapped to 5570 MHz cfg80211), AP2 on CH_12 (mapped to 5250 MHz cfg80211) — cross-channel ESS

What we tried (and what failed)

  1. wpa_cli_s1g -i wlan0 roam <target_bssid> — the link lands on the target AP momentarily (wlan0: associated to <new_bssid> in dmesg), but within ~3 seconds the AP sends a deauth and the STA falls back. Pattern repeats regardless of signal strength (tested at −40 / −50 dBm to both APs).

  2. wpa_cli_s1g -i wlan0 reassociate after set_network 0 bssid <target> + set_network 0 scan_freq <freq> — works reliably but performs a full WPA3-SAE handshake. End-to-end handoff takes ~2 s.

  3. PMKSA / OKC caching via pmksa_caching=1 okc=1 in the supplicant network block — set but observed minimal effect; the full handshake still seems to happen on every roam (likely because we are roaming between two different BSSIDs in different cells — we need OKC support on both APs and a shared PMK-R1 distribution mechanism, which we have not configured).

Questions

  1. Is 802.11r FT supported on MM8108 with morse_driver + hostapd_s1g/wpa_supplicant_s1g 2.12-morse-rel_1_16_4_2025_Sep_18 in the 2.9.3 OpenWrt release? If supported in a newer release (driver 1.17.x?), which one?

  2. If FT is supported, what is the correct hostapd_s1g configuration to enable it across two HaLow APs? Specifically:

    • mobility_domain=XXXX requirements

    • r0_key_holder / r1_key_holder MAC selection

    • ft_psk_generate_local=1 vs static r0kh/r1kh entries

    • PMK-R1 distribution requirement — do both APs need to be on the same wired LAN, or does FT-over-DS work over the air?

  3. What key_mgmt= should the STA supplicant advertise? FT-SAE only, or FT-SAE SAE for fallback?

  4. Is the “lands then deauths within ~3 s” symptom a known issue on a specific firmware/driver version? Is there a known fix or workaround?

  5. If FT is not supported on 2.9.3, what is the recommended fast-roam mechanism for sub-200 ms STA handoffs between two HaLow APs in an ESS on this platform? (Plain PMKSA + OKC, or 802.11v BTM?)

  6. Are there any driver/firmware logs we should capture (e.g., dmesg | grep morse, wpa_supplicant -dd, or a debug-enabled morse_driver build) that would help diagnose the wpa_cli roam failure mode?

Our current fallback (full reassociate + 2 s settle to detect the deauth) gives ~2 s total handoff. We need to reduce this significantly for streaming use cases that cannot tolerate 2 s gaps.

Best regards,

Hey @Asaftv

Sorry for the late reply here.

Yes. Strictly speaking, for Wi-Fi HaLow you should be using FT-SAE. However, our internal documentation - which might be a little out of date - is recommending to use FT-PSK for fast roaming support.
Enabling ieee80211r should be as simple as setting option ieee80211r 1 in /etc/config/wireless for the HaLow interface. This will set up some defaults for the configuration you have described in question 2.

You will also want to configure the r0kh and r1kh fields as per the OpenWrt /etc/config/wireless documentation

Please try with key_mgmt = PSK first. I would be interested to know if FT-SAE works when you adjust the pwe as below.

In /etc/config/wireless, try setting option sae_pwe '0' for the HaLow interface.

A sniffer capture would be very, very beneficial https://www.morsemicro.com/resources/appnotes/MM_APPNOTE-36_How_to_do_a_Sniffer_Capture.pdf, as would hostap logs from the two access points. Easiest way to configure debug logging in OpenWrt is to set option log_level '0' in /etc/config/wireless on the wifi-device of type ‘morse’. Read back these logs with logread -e hostapd_s1g.

I haven’t had a chance to experiment with this myself, but it has been documented as functional internally in the past. If all else fails, compare the generated hostap configuration - see /var/run/hostapd-phy#-wlan0.conf with the following configuration below:

ctrl_interface=/var/run/hostapd_s1g

interface=wlan0
driver=nl80211
hw_mode=a
ieee80211ah=1
channel=42
op_class=69
country_code=AU
s1g_prim_chwidth=1
s1g_prim_1mhz_chan_index=0

ssid=MorseMicroSamC
beacon_int=100
dtim_period=1
ap_max_inactivity=65536
s1g_capab=[SHORT-GI-ALL]

wpa=2
wpa_key_mgmt=FT-PSK
rsn_pairwise=CCMP

wpa_passphrase=12345678
ieee80211w=1

# Low priority / AC_BK = background
tx_queue_data3_aifs=7
tx_queue_data3_cwmin=15
tx_queue_data3_cwmax=1023
tx_queue_data3_burst=15.0

# Normal priority / AC_BE = best effort
tx_queue_data2_aifs=3
tx_queue_data2_cwmin=15
tx_queue_data2_cwmax=1023
tx_queue_data2_burst=15.0

# High priority / AC_VI = video
tx_queue_data1_aifs=2
tx_queue_data1_cwmin=7
tx_queue_data1_cwmax=15
tx_queue_data1_burst=15.0

# Highest priority / AC_VO = voice
tx_queue_data0_aifs=2
tx_queue_data0_cwmin=3
tx_queue_data0_cwmax=7
tx_queue_data0_burst=15.0

# Low priority / AC_BK = background
wmm_ac_bk_aifs=7
wmm_ac_bk_cwmin=4
wmm_ac_bk_cwmax=10
wmm_ac_bk_txop_limit=469
wmm_ac_bk_acm=0

# Normal priority / AC_BE = best effort
wmm_ac_be_aifs=3
wmm_ac_be_cwmin=4
wmm_ac_be_cwmax=10
wmm_ac_be_txop_limit=469
wmm_ac_be_acm=0

# High priority / AC_VI = video
wmm_ac_vi_aifs=2
wmm_ac_vi_cwmin=3
wmm_ac_vi_cwmax=4
wmm_ac_vi_txop_limit=469
wmm_ac_vi_acm=0

# Highest priority / AC_VO = voice
wmm_ac_vo_aifs=2
wmm_ac_vo_cwmin=2
wmm_ac_vo_cwmax=3
wmm_ac_vo_txop_limit=469
wmm_ac_vo_acm=0
wnm_sleep_mode=1

mobility_domain=a1b2
ft_psk_generate_local=1

r1_key_holder=0CBF7400017C
nas_identifier=0CBF7400017C

r0_key_lifetime=10000

r0kh=<mac addr1> <nasid1> <256 bit key>
r1kh=<mac addr2> <r1khid1> <256 bit key>
r0kh=<mac addr1> <nasid2> <256 bit key>
r1kh=<mac addr2> <r1khid2> <256 bit key>

CSA (Channel Switch Announcement) on MM8108 — hostapd_fill_csa_settings
emits IE with op_bw=0, prim_bw=0 — workaround / fixed release?

Setup:
Hardware: MM8108 EKH01 module
Software: 2.12-morse-rel_1_16_4_2025_Sep_18 hostapd_s1g / wpa_supplicant_s1g
(OpenWrt 2.9.3 release, kernel 5.15.167)
Mode: 8 MHz HaLow AP @ CH_28 (916 MHz HaLow / 5570 MHz mapped) with
WDS+SAE+sae_pwe=1 (default)

Symptom:
Issuing hostapd_cli_s1g -i wlan0 chan_switch 5 916 sec_channel_offset=0 center_freq1=916 bandwidth=8 op_class=68 returns success at the API,
but logread -e hostapd_s1g shows:

hostapd_s1g: IEEE 802.11 CHAN_SWITCH EHT/HE/VHT config 0x2
hostapd_s1g: hostapd_fill_csa_settings : ECSA info op_bw=0,
prim_bw=0, vht=0, ht=0

The CSA IE in the subsequent beacons is therefore emitted with
zero-filled bandwidth fields. Downstream STAs (MM8108 with the same
build, in managed mode) do NOT follow the announcement — empirically
they wait the full natural beacon-loss timeout (~12-15 s on this build
with listen_interval=5) before discovering the AP on the new channel.

As a result the only reliable way we have found to move the radio is
uci set wireless.radio0.channel=<X>; uci commit wireless; wifi reload,
which causes a 5-7 s wifi reload window during which the STA has no
way of knowing where the AP is going — total downtime 18-22 s on
affected STAs.

Questions:

  1. Is the op_bw=0/prim_bw=0/vht=0/ht=0 path through
    hostapd_fill_csa_settings() a known bug in
    2.12-morse-rel_1_16_4_2025_Sep_18 hostapd_s1g for S1G mode?

  2. What is the recommended invocation of chan_switch (or any other
    CLI / netlink path) for an S1G AP such that the emitted CSA IE
    contains correct bandwidth information AND downstream MM8108 STAs
    follow the announcement?

  3. Has this been fixed in any newer driver / hostapd_s1g release
    (1.17.x source line)? If so, which release, and is there a build
    available from the OpenWrt feed at upgrade.morsemicro.com?

  4. Are there hostapd_s1g build / log options that would surface
    additional diagnostics for the CSA IE path beyond
    option log_level '0' on the wifi-device?

We currently work around the bug by broadcasting a UDP CSA-OOB
announcement on the bridged LAN before issuing the channel change,
but this only helps STAs that run our custom listener and adds an
out-of-band failure mode (broadcast lost during the wifi reload
window). A working in-band CSA via the standard 802.11h flow would
obsolete the workaround.

Thanks,

This sounds like a separate set of questions to the fast roaming and should be a new topic.

Were you able to get 802.11r working as per my response? Were you able to capture a sniffer trace?

Regarding csa, check this thread for a command which worked: ECSA - Extended Channel Switch Announcement is not working

Hi,
Thank you for your Help!

Hi Morse team,

Following up on the community thread:

We applied ajudge’s recommended chan_switch syntax (freq in kHz,
prim_bandwidth, sec_channel_offset, no op_class) and the AP-side
behaviour is now correct — the ECSA IE is filled properly. However,
the STA-side mac80211 rejects the announcement at the kernel level
and disconnects.

Snippets of evidence below; full hostapd-phy0-wlan0.conf and full
logread excerpts are at the end of this mail.

== Hardware / firmware (both AP and STA) ==

Hardware: MM8108 EKH01
Build: rel_1_17_8_2026_Mar_24

  • hostapd_s1g rel_1_17_8_2026_Mar_24-1
  • wpa_supplicant_s1g rel_1_17_8_2026_Mar_24-1
  • kmod-morse 5.15.189+rel_1_17_8_2026_Mar_24-1
  • morse-fw-8108 rel_1_17_8_2026_Mar_24-1
  • morsectrl rel_1_17_8_2026_Mar_24-1
  • wpad-openssl 2023-09-08-e5ccbfc6-8
  • morse-regdb v2.7.0-1
  • kernel 5.15.189
  • iw 5.19
    Regulatory: country=US, 8 MHz HaLow

== Test setup ==

AP wlan0 mode=ap, S1G op_chan 28 (HaLow center 916 MHz, 8 MHz BW),
SAE encryption with sae_pwe=0, FT-SAE enabled
(ieee80211r=1, mobility_domain=a1b2, ft_psk_generate_local=0,
nas_identifier=, r1_key_holder=,
reassociation_deadline=20000, ieee80211k=1,
rrm_neighbor_report=1, rrm_beacon_report=1)
STA wlan0 mode=managed, associated to the AP via SAE / FT-SAE
(auth_alg=ft observed for reassociations - FT itself works)

== chan_switch command ==

$ hostapd_cli_s1g -i wlan0 chan_switch 10 920500
prim_bandwidth=1 sec_channel_offset=1
center_freq1=924000 bandwidth=8
OK

Switching from S1G op_chan 28 (HaLow center 916 MHz)
to S1G op_chan 44 (HaLow center 924 MHz).
Lower-edge 1 MHz subchannel for CH_44 is at 920.5 MHz =
920500 kHz, per the rule “<freq_kHz> = <center_kHz> -
(bw_MHz - 1) × 500” we derived from your community example.

== AP-side: WORKS as expected ==

hostapd_s1g: wlan0: IEEE 802.11 CHAN_SWITCH EHT config 0x2
HE config 0x2 VHT config 0x1
hostapd_s1g: hostapd_fill_csa_settings : ECSA info op_bw=160,
prim_bw=1, vht=1, ht=1, change to oldconfig:
ht=1, vht=1

ECSA IE bandwidth fields are non-zero. The radio moves in place
and iw dev wlan0 info post-switch shows:

channel 149 (5745 MHz), width: 160 MHz, center1: 5815 MHz

This is the fix for the original “op_bw=0 prim_bw=0” report — that
was our wrong invocation (freq in MHz, omitted prim_bandwidth,
extra op_class). Your community example’s syntax is correct.

== STA-side: REJECTED by mac80211 ==

With CSA-OOB workaround DISABLED (so the STA only sees the in-band
CSA, no application-layer pre-arm), the STA kernel logs:

[4414.171472] wlan0: AP 0c:bf:74:00:1f:05 switches to
unsupported channel (5745.000 MHz, width:5,
CF1/2: 5815.000/0 MHz), disconnecting

immediately followed by:

wpa_supplicant_s1g: wlan0: CTRL-EVENT-DISCONNECTED
bssid=0c:bf:74:00:1f:05 reason=4
locally_generated=1

width:5 is NL80211_CHAN_WIDTH_160. Our reading: the same
cfg80211 compat shim that maps the S1G 8 MHz block to a 5 GHz
160 MHz channel for IE generation also drives the STA-side
validation - and mac80211 then rejects the channel under the
US 5 GHz regulatory domain (which doesn’t permit this 160 MHz
pair), even though the S1G regdom that actually applies on the
wire DOES permit it.

Net effect: STA disconnects, falls into beacon-loss timeout,
full-scans, reassociates ~28 s later (measured). We work
around it with an application-layer UDP “CSA-OOB” pre-arm that
pushes the new freq to the STA before the radio moves; with
that, recovery drops to ~5-8 s. The workaround is what we’re
shipping now.

== Questions ==

  1. Is the STA-side “switches to unsupported channel …
    width:5” rejection a known issue on rel_1_17_8_2026_Mar_24?
    Does a newer build let the STA’s mac80211 follow the in-band
    CSA on an S1G 8 MHz block without flagging it as 5 GHz /
    160 MHz?

  2. Is there a driver / kernel flag (morse driver side, or a
    regulatory option at boot) that makes the STA’s regdomain
    check happen against the S1G plan rather than the
    cfg80211-mapped 5 GHz plan?

  3. If cfg80211 compat is the long-term plan, is there a
    mac80211 / cfg80211 patch in your tree that accepts the
    mapped 160 MHz width under regdom=US for the S1G channel
    range during a CSA follow?

== What we’d appreciate ==

  • Confirmation that our AP-side ECSA invocation is correct
    (we’re sending the IE with non-zero bandwidth fields).
  • Status on the STA-side rejection (known issue / fix planned
    / fix shipped after rel_1_17_8_2026_Mar_24).
  • Pointer to release notes if the STA-side fix is in a newer
    build we haven’t picked up.

Sniffer capture available on request - the snippets above are the
load-bearing lines but a 60-second pcap around the chan_switch is
ready to share.

Thanks,

───────────────── APPENDIX A: AP hostapd-phy0-wlan0.conf ─────────────────

driver=nl80211
logger_syslog=127
logger_syslog_level=2
logger_stdout=127
logger_stdout_level=2
country_code=US
hw_mode=a
channel=28
chanlist=28
op_class=4
s1g_capab=[SHORT-GI-ALL]
s1g_prim_chwidth=1
s1g_prim_1mhz_chan_index=3

ieee80211ah=1

anti-clogging does not work and creates issues in high station count networks (100+)

This effectively disables it

anti_clogging_threshold=99999

interface=wlan0
ctrl_interface=/var/run/hostapd_s1g
ap_isolate=1
bss_load_update_period=60
chan_util_avg_period=600
disassoc_low_ack=0
skip_inactivity_poll=0
preamble=1
wmm_enabled=1
ignore_broadcast_ssid=0
uapsd_advertisement_enabled=1
utf8_ssid=1
multi_ap=0
beacon_int=100
wpa_group_rekey=604800
nas_identifier=0cbf74001f05
sae_require_mfp=1
wpa_passphrase=
sae_password=
wpa_psk_file=/var/run/hostapd-wlan0.psk
auth_algs=1
wpa=2
wpa_pairwise=CCMP
ssid=g7-AP-00000001
bridge=br-lan
wds_bridge=
wnm_sleep_mode=1
bss_transition=1
rrm_neighbor_report=1
rrm_beacon_report=1
mobility_domain=a1b2
ft_psk_generate_local=0
ft_over_ds=0
reassociation_deadline=20000
r1_key_holder=0cbf74001f05
r0_key_lifetime=10000
pmk_r1_push=0
r0kh=ff:ff:ff:ff:ff:ff * 80ab93aff49137476a8acededb853f3a
r1kh=00:00:00:00:00:00 00:00:00:00:00:00 80ab93aff49137476a8acededb853f3a
wpa_disable_eapol_key_retries=0
wpa_key_mgmt=SAE FT-SAE
okc=1
ieee80211w=2
group_mgmt_cipher=AES-128-CMAC
qos_map_set=0,0,2,16,1,1,255,255,18,22,24,38,40,40,44,46,48,56

(TX queue / WMM blocks omitted for brevity — defaults)

wds_sta=1
bssid=0c:bf:74:00:1f:05
sae_pwe=0
dpp_configurator_connectivity=1
raw=0

───────────────── APPENDIX B: STA kernel + supplicant log ─────────────────
(captured with CSA-OOB workaround DISABLED so only the in-band CSA path
is exercised; T0 = chan_switch issued on the AP)

[T0]
kern.info kernel: [4390.369166] wlan0: deauthenticating from
0c:bf:74:00:1f:05 by local choice
(Reason: 2=PREV_AUTH_NOT_VALID)
daemon.notice wpa_supplicant_s1g[7177]: wlan0: SME: Deauth request to
the driver failed

[T0 + ~23 s — STA receives the CSA IE and tries to follow]
kern.info kernel: [4414.171472] wlan0: AP 0c:bf:74:00:1f:05 switches to
unsupported channel (5745.000 MHz, width:5,
CF1/2: 5815.000/0 MHz), disconnecting
daemon.notice wpa_supplicant_s1g[7177]: wlan0: CTRL-EVENT-DISCONNECTED
bssid=0c:bf:74:00:1f:05 reason=4 locally_generated=1
daemon.notice wpa_supplicant_s1g[7177]: wlan0: Added BSSID
0c:bf:74:00:1f:05 into ignore list, ignoring for
10 seconds

[T0 + ~28 s — STA full-scans and reassociates from scratch]
daemon.notice wpa_supplicant_s1g[8014]: wlan0: Associated with
0c:bf:74:00:1f:05
daemon.notice wpa_supplicant_s1g[8014]: wlan0: CTRL-EVENT-CONNECTED -
Connection to 0c:bf:74:00:1f:05 completed [id=0]

───────────────── APPENDIX C: AP hostapd log (chan_switch path) ─────────────────

daemon.info hostapd_s1g: wlan0: IEEE 802.11 CHAN_SWITCH EHT config 0x2
HE config 0x2 VHT config 0x1
daemon.notice hostapd_s1g: hostapd_fill_csa_settings : ECSA info
op_bw=160, prim_bw=1, vht=1, ht=1,
change to oldconfig: ht=1, vht=1

Thanks,

Hi Ajudge,
Thank you for your support.

802.11r FT — “Failed to set PTK to the driver” on MM8108 aarch64 build (FT-PSK + FT-SAE both affected)
Hi Morse Micro Support Team,
Following your 2026-06 reply confirming 802.11r FT is supported on MM8108 with hostapd_s1g/wpa_supplicant_s1g 2.12-morse-rel_1_16_4_2025_Sep_18 + sae_pwe=0, we worked through the recommended FT config in a multi-AP HaLow deployment. The sae_pwe=0 fix resolved the original ~3-s post-roam deauth we reported. However, we are now blocked by a second, deeper problem: the FT auth completes over-the-air but the FT-derived PTK cannot be installed into the driver via nl80211 SET_KEY — both FT-PSK and FT-SAE are affected.
Summary


Test
|
Result
|

  • | - |

    FT-SAE with sae_pwe=0 + your full recommended config
    |
    :cross_mark: FT: Failed to set PTK to the driver → fallback to plain SAE
    |

    FT-PSK (exact recipe from your 2026-06 sample: wpa_key_mgmt=FT-PSK, ft_psk_generate_local=1, mobility_domain, r0_key_lifetime, r1_key_holder, nas_identifier)
    |
    :cross_mark: Same FT: Failed to set PTK to the driver → fallback to plain PSK
    |

    Plain SAE / plain PSK without FT (control)
    |
    :white_check_mark: works, roam takes ~1.5–5 s (full 4-way each time)
    |

Both FT variants fail at the same step in the supplicant. Switching FT-SAE → FT-PSK changed nothing — the error is at the nl80211 SET_KEY layer, not the over-the-air handshake.
Reproduction — exact kernel error: nl80211: kernel reports: key not allowed
The full logread capture below makes the failure unambiguous: wpa_supplicant_s1g requests the FT-derived PTK via nl80211 SET_KEY, the kernel returns key not allowed, and wpa_supplicant_s1g logs FT: Failed to set PTK to the driver and tears the connection down. The next-AP reassoc is then rejected by that AP with status_code=55 (“Auth rejected”) because the FT-MD context from the prior failure is stale, and after one retry the STA falls back to plain PSK and the link comes up.


# STA .201 — single end-to-end roam attempt, FT-PSK config, no edits
# (Sun Jun 14 18:25:43 — see appendix A for the full capture)

[10687.907649] kernel:  wlan0: authenticate with 50:2e:91:d2:c6:59
[10687.924135] kernel:  wlan0: send auth to 50:2e:91:d2:c6:59 (try 1/3)
[18:25:43] wpa_supplicant_s1g: nl80211: kernel reports: key not allowed   <-- KERNEL  <===
[18:25:43] wpa_supplicant_s1g: FT: Failed to set PTK to the driver        <-- SUPPLICANT
[18:25:43] wpa_supplicant_s1g: Trying to associate with 50:2e:91:d2:c6:59
[10687.941434] kernel:  wlan0: authenticated
[10687.954733] kernel:  wlan0: associate with 50:2e:91:d2:c6:59 (try 1/3)
[10687.975458] kernel:  wlan0: RX ReassocResp from 50:2e:91:d2:c6:59 (capab=0x1011 status=0 aid=1)
                                                                          ↑ first AP accepted plain reassoc
[18:25:43] wpa_supplicant_s1g: Associated with 50:2e:91:d2:c6:59
[18:25:43] wpa_supplicant_s1g: WPA: Key negotiation completed [PTK=CCMP GTK=CCMP]
[18:25:43] wpa_supplicant_s1g: CTRL-EVENT-CONNECTED

# 0.1 s later — STA tries to roam to the next AP. FT path same failure:

[10688.105163] kernel:  wlan0: disconnect from AP 50:2e:91:d2:c6:59 for new auth to 50:2e:91:d2:ce:54
[10688.237350] kernel:  wlan0: authenticate with 50:2e:91:d2:ce:54
[10688.253749] kernel:  wlan0: send auth (try 1/3)
[10688.364684] kernel:  wlan0: send auth (try 2/3)
[10688.474685] kernel:  wlan0: send auth (try 3/3)
[18:25:44] wpa_supplicant_s1g: nl80211: kernel reports: key not allowed   <-- KERNEL  <===
[18:25:44] wpa_supplicant_s1g: FT: Failed to set PTK to the driver        <-- SUPPLICANT
[10688.498864] kernel:  wlan0: authenticated
[10688.504714] kernel:  wlan0: associate (try 1/3)
[10688.525432] kernel:  wlan0: RX ReassocResp ... status=55 aid=1         <-- AP REJECTS
[10688.533458] kernel:  wlan0: 50:2e:91:d2:ce:54 denied association (code=55)
[18:25:44] wpa_supplicant_s1g: CTRL-EVENT-ASSOC-REJECT status_code=55
[18:25:44] wpa_supplicant_s1g: SME: Deauth request to the driver failed
[18:25:44] wpa_supplicant_s1g: Added BSSID 50:2e:91:d2:ce:54 into ignore list, 10 seconds

# 1 s later supplicant retries from scratch (non-FT) — and succeeds:
[18:25:45] wpa_supplicant_s1g: Trying to authenticate with 50:2e:91:d2:ce:54
[18:25:45] wpa_supplicant_s1g: RX AssocResp ... status=0 aid=1
[18:25:45] wpa_supplicant_s1g: WPA: Key negotiation completed [PTK=CCMP GTK=CCMP]
[18:25:45] wpa_supplicant_s1g: CTRL-EVENT-CONNECTED

Two distinct symptoms in one trace, both rooted at the same kernel reply:

  1. nl80211: kernel reports: key not allowedwpa_supplicant_s1g issues NL80211_CMD_NEW_KEY (or SET_KEY) for the FT-derived PTK; mac80211 hands it to morse.ko’s .set_key() callback; the driver returns -EPERM (“Operation not permitted”). The supplicant prints Failed to set PTK to the driver.

  2. status=55 (“Auth rejected”) on the AP-side ReassocResp — the second AP refuses the FT ReassocReq, which suggests the AP-side hostapd hits the same kernel error when it tries to install the PMK-R1 it derived from our FT request. Same code path, mirrored.

The same key not allowedFailed to set PTK pattern reproduces on every FT-PSK roam, regardless of which of the three APs is the target. Plain PSK association (no FT) never hits this — the post-association 4-way SET_KEY works fine.
Hardware / software baseline


Role
|
IP
|
Board
|
Kernel
|
Driver
|

  • | - | - | - | - |

    STA
    |
    192.168.1.201
    |
    EKH01 (MM8108)
    |
    5.15.189 aarch64
    |
    morse.ko 678376 B, dated Apr 2 2026 (stock, identical to /rom/)
    |

    AP
    |
    192.168.1.203
    |
    EKH01 (MM8108)
    |
    5.15.167 aarch64
    |
    morse.ko stock 641280 B, dated Sep 19 2025
    |

    AP
    |
    192.168.1.200
    |
    Older MM6108 carrier
    |
    5.15.189 MIPS32
    |
    morse.ko stock
    |

    AP
    |
    192.168.1.202
    |
    Older MM6108 carrier
    |
    5.15.189 MIPS32
    |
    morse.ko stock
    |

hostapd_s1g / wpa_supplicant_s1g packaged build (visible in logread): rel_1_17_8_2026_Mar_24.
The “Failed to set PTK to the driver” reproduces on the STA (.201, aarch64). All three APs are configured identically with the FT options below; the same STA roaming between the three APs (.200, .202, .203) produces the same error in every direction.
Config in use
AP side (hostapd via uci → /var/run/hostapd-phy*.conf)


wpa_key_mgmt = WPA-PSK FT-PSK                   # we also tried SAE FT-SAE - same result
ieee80211r = 1
mobility_domain = a1b2
r1_key_holder = <AP-MAC-no-colons>
nas_identifier = <AP-MAC-no-colons>
r0_key_lifetime = 10000
reassociation_deadline = 20000
ft_psk_generate_local = 1                       # 1 for FT-PSK (your sample); 0 for FT-SAE
pmk_r1_push = 0
ft_over_ds = 0
sae_require_mfp = 1
sae_pwe = 0                                      # your 2026-06 fix
disable_pmksa_caching = 0
okc = 1
bss_transition = 1
ieee80211k = 1
rrm_neighbor_report = 1
rrm_beacon_report = 1
disassoc_low_ack = 0

STA side (supplicant.conf, generated by netifd from uci)


country = US
sae_pwe = 0
network = {
    key_mgmt = WPA-PSK FT-PSK
    psk = "<32-char>"
    pairwise = CCMP
    proto = RSN
    ieee80211w = 2
    bssid = <target>
    scan_ssid = 1
    ...
}

Netifd patch applied
hostapd_s1g rejects netifd’s auto-added ft_iface=br-lan with 1 errors found in configuration file '/var/run/hostapd-phy0-wlan0.conf' and hostapd dies. We had to comment out the ft_iface= line in both:

  • /lib/netifd/hostapd.sh line 934 / 941 (depending on package version)
  • /lib/netifd/morse/morse_overrides.sh line 432 / 434
    Without these patches hostapd_s1g cannot come up at all when ieee80211r=1. We assume this is a known netifd-vs-hostapd_s1g mismatch — please let us know if there is an official fix or supported way to disable the ft_iface= line.
    Secondary issue — chan_switch to CH_44 (US 8 MHz) fails on aarch64 build only
    While testing channel switching with the same boards:

hostapd_cli_s1g -i wlan0 chan_switch 30 921000 \
    prim_bandwidth=2 sec_channel_offset=1 \
    center_freq1=924000 bandwidth=8
→ FAIL

logread on the AP:


hostapd_s1g: nl80211: kernel reports: (extension) channel is disabled
hostapd_s1g: Channel 44 not found for s1g/global operating class 1/68
hostapd_s1g: Failed to derive class from s1g primary bandwidth

The exact same command on the MIPS AP (.200) returns OK and the radio retunes correctly to CH_44 (5815 MHz mapped). On the aarch64 APs (.203 / EKH01) the kernel rejects it with the “extension channel is disabled” / “Channel 44 not found in operating class 1/68” pair.
The Morse self-managed regdom shows the channel as allowed:


phy#0 (self-managed)
country US: DFS-invalid
    (920 - 928 @ 8), (N/A, 36), (N/A), AUTO-BW       <-- CH_44 block, allowed

CH_12 and CH_28 chan_switch commands work fine on the same aarch64 board with identical syntax. Only CH_44 is rejected.
Note that the s1g_freq / cfg80211 freq state on aarch64 hostapd_cli status sometimes returns inconsistent values after a failed chan_switch (e.g. s1g_freq=916000 but channel=100 freq=5500) until wifi down && wifi up resets it — suggesting hostapd’s internal state isn’t being cleaned up when the kernel rejects the switch.
What we’d like from you

  1. The PTK install error. Is FT: Failed to set PTK to the driver a known issue in the aarch64 morse.ko build? Is there a driver build that addresses it? If the issue is in our config, what is wrong — the wpa_supplicant_s1g derives the FT PTK successfully (it just can’t install it via nl80211 SET_KEY), so this looks driver-side to us.

  2. ft_iface= — what is the supported way to disable netifd’s auto-add of this option on Morse builds? Our patch works but is invasive.

  3. CH_44 chan_switch — is the aarch64 hostapd_s1g operating class table out of sync with the Morse regdom for the upper 8 MHz US block? Channel 44 not found for s1g/global operating class 1/68 looks like a missing entry rather than a regulatory restriction.

  4. status=55 from the AP on the FT ReassocResp — does the AP-side hostapd also fail to install the FT-derived PMK-R1, mirroring the STA-side Failed to set PTK? If yes, the bug is symmetric and pointing at a common morse.ko code path.

We can provide:

  • Full logread from STA + AP from a single reproduction
  • dmesg from the STA at the time of the failed SET_KEY
  • The exact /var/run/hostapd-phy*.conf and /var/run/wpa_supplicant-wlan0.conf produced by netifd
  • A packet capture from a third radio if useful (we’d need guidance on what frames to focus on)
    Happy to schedule a call or share a sniffer trace. Thanks in advance for any pointers.
    Best regards,
    asaf@guardian7.co Guardian7 — EKH01 deployment
    Appendix A — Full STA logread (.201) around the FT failure

# STA .201 — wpa_supplicant_s1g + kernel mac80211 — single ROAM attempt
# Sun Jun 14 18:25:43–48 2026

Sun Jun 14 18:25:43 daemon.notice wpa_supplicant_s1g[30240]: wlan0: SME: Trying to authenticate with 50:2e:91:d2:c6:59 (SSID='g7-AP' chan=37)
Sun Jun 14 18:25:43 kern.info  kernel: [10687.885622] wlan0: disconnect from AP 50:2e:91:d2:ce:54 for new auth to 50:2e:91:d2:c6:59
Sun Jun 14 18:25:43 kern.info  kernel: [10687.907649] wlan0: authenticate with 50:2e:91:d2:c6:59
Sun Jun 14 18:25:43 daemon.notice netifd: Network device 'wlan0' link is down
Sun Jun 14 18:25:43 kern.info  kernel: [10687.924135] wlan0: send auth to 50:2e:91:d2:c6:59 (try 1/3)
Sun Jun 14 18:25:43 daemon.err   wpa_supplicant_s1g[30240]: nl80211: kernel reports: key not allowed
Sun Jun 14 18:25:43 daemon.warn  wpa_supplicant_s1g[30240]: FT: Failed to set PTK to the driver
Sun Jun 14 18:25:43 daemon.notice wpa_supplicant_s1g[30240]: wlan0: Trying to associate with 50:2e:91:d2:c6:59 (SSID='g7-AP' chan=37)
Sun Jun 14 18:25:43 kern.info  kernel: [10687.941434] wlan0: authenticated
Sun Jun 14 18:25:43 kern.info  kernel: [10687.954733] wlan0: associate with 50:2e:91:d2:c6:59 (try 1/3)
Sun Jun 14 18:25:43 kern.info  kernel: [10687.975458] wlan0: RX ReassocResp from 50:2e:91:d2:c6:59 (capab=0x1011 status=0 aid=1)
Sun Jun 14 18:25:43 daemon.notice netifd: Network device 'wlan0' link is up
Sun Jun 14 18:25:43 daemon.notice wpa_supplicant_s1g[30240]: wlan0: Associated with 50:2e:91:d2:c6:59
Sun Jun 14 18:25:43 daemon.notice wpa_supplicant_s1g[30240]: wlan0: WPA: Key negotiation completed with 50:2e:91:d2:c6:59 [PTK=CCMP GTK=CCMP]
Sun Jun 14 18:25:43 daemon.notice wpa_supplicant_s1g[30240]: wlan0: CTRL-EVENT-CONNECTED - Connection to 50:2e:91:d2:c6:59 completed [id=0 id_str=]
Sun Jun 14 18:25:43 daemon.notice wpa_supplicant_s1g[30240]: wlan0: CTRL-EVENT-SUBNET-STATUS-UPDATE status=0
Sun Jun 14 18:25:43 kern.info  kernel: [10687.986874] wlan0: associated

# 100 ms later — roam to the next AP

Sun Jun 14 18:25:43 daemon.notice wpa_supplicant_s1g[30240]: wlan0: SME: Trying to authenticate with 50:2e:91:d2:ce:54 (SSID='g7-AP' chan=11)
Sun Jun 14 18:25:43 kern.info  kernel: [10688.105163] wlan0: disconnect from AP 50:2e:91:d2:c6:59 for new auth to 50:2e:91:d2:ce:54
Sun Jun 14 18:25:43 kern.info  kernel: [10688.237350] wlan0: authenticate with 50:2e:91:d2:ce:54
Sun Jun 14 18:25:43 kern.info  kernel: [10688.253749] wlan0: send auth to 50:2e:91:d2:ce:54 (try 1/3)
Sun Jun 14 18:25:44 kern.info  kernel: [10688.364684] wlan0: send auth to 50:2e:91:d2:ce:54 (try 2/3)
Sun Jun 14 18:25:44 kern.info  kernel: [10688.474685] wlan0: send auth to 50:2e:91:d2:ce:54 (try 3/3)
Sun Jun 14 18:25:44 daemon.err   wpa_supplicant_s1g[30240]: nl80211: kernel reports: key not allowed
Sun Jun 14 18:25:44 daemon.warn  wpa_supplicant_s1g[30240]: FT: Failed to set PTK to the driver
Sun Jun 14 18:25:44 daemon.notice wpa_supplicant_s1g[30240]: wlan0: Trying to associate with 50:2e:91:d2:ce:54 (SSID='g7-AP' chan=11)
Sun Jun 14 18:25:44 daemon.notice netifd: Network device 'wlan0' link is down
Sun Jun 14 18:25:44 kern.info  kernel: [10688.498864] wlan0: authenticated
Sun Jun 14 18:25:44 kern.info  kernel: [10688.504714] wlan0: associate with 50:2e:91:d2:ce:54 (try 1/3)
Sun Jun 14 18:25:44 kern.info  kernel: [10688.525432] wlan0: RX ReassocResp from 50:2e:91:d2:ce:54 (capab=0x1011 status=55 aid=1)
Sun Jun 14 18:25:44 kern.info  kernel: [10688.533458] wlan0: 50:2e:91:d2:ce:54 denied association (code=55)
Sun Jun 14 18:25:44 daemon.notice wpa_supplicant_s1g[30240]: wlan0: CTRL-EVENT-ASSOC-REJECT bssid=50:2e:91:d2:ce:54 status_code=55
Sun Jun 14 18:25:44 daemon.notice wpa_supplicant_s1g[30240]: wlan0: SME: Deauth request to the driver failed
Sun Jun 14 18:25:44 daemon.notice wpa_supplicant_s1g[30240]: wlan0: Added BSSID 50:2e:91:d2:ce:54 into ignore list, ignoring for 10 seconds

# 1 second later — supplicant retries non-FT, succeeds with plain PSK:

Sun Jun 14 18:25:45 daemon.notice wpa_supplicant_s1g[30240]: wlan0: Removed BSSID 50:2e:91:d2:ce:54 from ignore list (clear)
Sun Jun 14 18:25:45 daemon.notice wpa_supplicant_s1g[30240]: wlan0: SME: Trying to authenticate with 50:2e:91:d2:ce:54 (SSID='g7-AP' chan=11)
Sun Jun 14 18:25:45 kern.info  kernel: [10689.606109] wlan0: authenticate with 50:2e:91:d2:ce:54
Sun Jun 14 18:25:45 kern.info  kernel: [10689.622725] wlan0: send auth to 50:2e:91:d2:ce:54 (try 1/3)
Sun Jun 14 18:25:45 kern.info  kernel: [10689.633984] wlan0: authenticated
Sun Jun 14 18:25:45 daemon.notice wpa_supplicant_s1g[30240]: wlan0: Trying to associate with 50:2e:91:d2:ce:54 (SSID='g7-AP' chan=11)
Sun Jun 14 18:25:45 kern.info  kernel: [10689.644678] wlan0: associate with 50:2e:91:d2:ce:54 (try 1/3)
Sun Jun 14 18:25:45 daemon.notice netifd: Network device 'wlan0' link is up
Sun Jun 14 18:25:45 kern.info  kernel: [10689.662664] wlan0: RX AssocResp from 50:2e:91:d2:ce:54 (capab=0x1011 status=0 aid=1)
Sun Jun 14 18:25:45 kern.info  kernel: [10689.673898] wlan0: associated
Sun Jun 14 18:25:45 daemon.notice wpa_supplicant_s1g[30240]: wlan0: Associated with 50:2e:91:d2:ce:54
Sun Jun 14 18:25:45 daemon.notice wpa_supplicant_s1g[30240]: wlan0: WPA: Key negotiation completed with 50:2e:91:d2:ce:54 [PTK=CCMP GTK=CCMP]
Sun Jun 14 18:25:45 daemon.notice wpa_supplicant_s1g[30240]: wlan0: CTRL-EVENT-CONNECTED - Connection to 50:2e:91:d2:ce:54 completed

Appendix B — STA dmesg (.201) for the same window


[10657.896165] wlan0: authenticate with 50:2e:91:d2:ce:54
[10657.915153] wlan0: send auth to 50:2e:91:d2:ce:54 (try 1/3)
[10657.925915] wlan0: authenticated
[10657.934703] wlan0: associate with 50:2e:91:d2:ce:54 (try 1/3)
[10657.951413] wlan0: RX AssocResp from 50:2e:91:d2:ce:54 (capab=0x1011 status=0 aid=1)
[10657.962648] wlan0: associated
[10658.036240] IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready
[10687.885622] wlan0: disconnect from AP 50:2e:91:d2:ce:54 for new auth to 50:2e:91:d2:c6:59
[10687.907649] wlan0: authenticate with 50:2e:91:d2:c6:59
[10687.924135] wlan0: send auth to 50:2e:91:d2:c6:59 (try 1/3)
[10687.941434] wlan0: authenticated
[10687.954733] wlan0: associate with 50:2e:91:d2:c6:59 (try 1/3)
[10687.975458] wlan0: RX ReassocResp from 50:2e:91:d2:c6:59 (capab=0x1011 status=0 aid=1)
[10687.986874] wlan0: associated
[10688.105163] wlan0: disconnect from AP 50:2e:91:d2:c6:59 for new auth to 50:2e:91:d2:ce:54
[10688.237350] wlan0: authenticate with 50:2e:91:d2:ce:54
[10688.253749] wlan0: send auth to 50:2e:91:d2:ce:54 (try 1/3)
[10688.364684] wlan0: send auth to 50:2e:91:d2:ce:54 (try 2/3)
[10688.474685] wlan0: send auth to 50:2e:91:d2:ce:54 (try 3/3)
[10688.498864] wlan0: authenticated
[10688.504714] wlan0: associate with 50:2e:91:d2:ce:54 (try 1/3)
[10688.525432] wlan0: RX ReassocResp from 50:2e:91:d2:ce:54 (capab=0x1011 status=55 aid=1)
[10688.533458] wlan0: 50:2e:91:d2:ce:54 denied association (code=55)
[10689.606109] wlan0: authenticate with 50:2e:91:d2:ce:54
[10689.622725] wlan0: send auth to 50:2e:91:d2:ce:54 (try 1/3)
[10689.633984] wlan0: authenticated
[10689.644678] wlan0: associate with 50:2e:91:d2:ce:54 (try 1/3)
[10689.662664] wlan0: RX AssocResp from 50:2e:91:d2:ce:54 (capab=0x1011 status=0 aid=1)
[10689.673898] wlan0: associated

Appendix C — full hostapd-phy1-wlan0.conf (.200 AP)
Generated by netifd from the uci config shown in the main body. The AP is currently broadcasting ssid=g7-AP on channel=28 (US 8 MHz HaLow, mapped to 5570 MHz center). wpa_key_mgmt=WPA-PSK FT-PSK, mobility_domain=a1b2, ft_psk_generate_local=1, sae_pwe=0 — exactly the recipe from your 2026-06 reply.


driver=nl80211
country_code=US
hw_mode=a
channel=28
op_class=71
s1g_capab=[SHORT-GI-ALL]
s1g_prim_chwidth=1
s1g_prim_1mhz_chan_index=3
ieee80211ah=1
anti_clogging_threshold=99999

interface=wlan0
ctrl_interface=/var/run/hostapd_s1g
ap_isolate=1
disassoc_low_ack=0
ignore_broadcast_ssid=0
utf8_ssid=1
beacon_int=100
wpa_group_rekey=604800
nas_identifier=502e91d2c659
wpa_passphrase=8Fk7Tq2RmZ9pVnXc4HsLwB6jNyU3DeYa
sae_password=8Fk7Tq2RmZ9pVnXc4HsLwB6jNyU3DeYa
wpa_psk_file=/var/run/hostapd-wlan0.psk
auth_algs=1
wpa=2
wpa_pairwise=CCMP
ssid=g7-AP
bridge=br-lan
wnm_sleep_mode=1
bss_transition=1
rrm_neighbor_report=1
rrm_beacon_report=1
mobility_domain=a1b2
ft_psk_generate_local=1
ft_over_ds=0
reassociation_deadline=20000
wpa_disable_eapol_key_retries=0
wpa_key_mgmt=WPA-PSK FT-PSK
okc=0
disable_pmksa_caching=1
bssid=50:2e:91:d2:c6:59
sae_pwe=0

(WMM / TX queue / qos_map_set lines elided for brevity — they are the netifd defaults.)
Note: ft_iface= is NOT present in this file — we patched both /lib/netifd/hostapd.sh and /lib/netifd/morse/morse_overrides.sh to drop the auto-added ft_iface=$network_ifname line. Without that patch hostapd_s1g aborts with 1 errors found in configuration file and never reaches state=ENABLED.
Appendix D — full /var/run/wpa_supplicant-wlan0.conf (.201 STA)


country=US
ctrl_interface=/var/run/wpa_supplicant_s1g
sae_pwe=0

network={
    scan_ssid=1
    ssid="g7-AP"
    key_mgmt=WPA-PSK FT-PSK
    psk="8Fk7Tq2RmZ9pVnXc4HsLwB6jNyU3DeYa"
    pairwise=CCMP
    proto=RSN
    bssid=50:2e:91:d2:c6:59
}

The STA uci sets ieee80211r=1, which netifd translates into the WPA-PSK FT-PSK key_mgmt line above. ieee80211w (MFP) is intentionally not pinned — under FT-PSK it is optional and not advertised. The bssid= field is set per-roam by our Python orchestrator before each wpa_cli reassociate; the supplicant’s own bgscan would otherwise pick the strongest match in the BSS cache.
Appendix E — driver / firmware versions