Short version
On an 802.11s mesh, 128-bit ciphers work fine. 256-bit ciphers never form a link.
Same hardware, same firmware, same passphrase — the only thing I change is the cipher.
| Setting | Cipher used | Mesh link? |
|---|---|---|
encryption='sae' |
CCMP-128 | Works |
encryption='sae+gcmp' |
GCMP-128 | Works |
encryption='sae+gcmp256' |
GCMP-256 | Never links |
encryption='sae+ccmp256' |
CCMP-256 | Never links |
Both 128-bit options work and both 256-bit options fail, so it looks like the problem is
the 256-bit key length itself, not GCM vs CCM.
The chip says it supports these. iw phy0 info lists CCMP-256 and GCMP-256 alongside the
128-bit ones.
Setup
- Raspberry Pi 4, OpenWrt 23.05.5
- MM8108 (B2), SDIO. Driver log line at boot:
Loaded firmware from morse/mm8108b2-rl.bin
Morse Micro Dot11ah driver registration. Version 0-rel_1_16_4_2025_Sep_18 wpa_supplicant_s1g 1.16.4-2, morse-feed pinned at263b4c4(Aug 2025)- Two radios, 802.11ah 8 MHz, 802.11s mesh with batman-adv on top
- Signal −7 to −28 dBm, so this isn’t a weak-signal problem
First problem: the 256-bit setting is silently ignored
Before I could even test this, I found that setting a 256-bit cipher does nothing at all.
/lib/netifd/morse/morse_overrides.sh (around line 1055) only recognises two exact values:
[ "$wpa_cipher" = GCMP ] && { ...pairwise=GCMP... }
[ "$wpa_cipher" = CCMP ] && ...pairwise=CCMP...
GCMP-256 and CCMP-256 match neither, so no cipher line gets written and wpa_supplicant
quietly falls back to CCMP-128. No warning, no error — it just looks like nothing happened.
I think that’s a bug by itself. Even if 256-bit isn’t supported, it shouldn’t silently
downgrade.
I patched that locally to pass the value through, which is how I got to the real issue.
Second problem: with the cipher applied, the link never forms
$ wpa_cli_s1g -i wlan0 status
pairwise_cipher=GCMP-256
group_cipher=GCMP-256
wpa_state=COMPLETED <- looks fine
$ iw dev wlan0 station dump | grep 'mesh plink'
mesh plink: HOLDING <- never reaches ESTAB
$ batctl n
(no neighbours)
Looking at the keys actually loaded into the driver:
idx1 00-0f-ac:9 32B <- the 32-byte group key installs fine
<- but no idx0 entry: the per-link key is never created
So the 256-bit group key works. The per-link key never gets made, because the two
radios never finish their handshake.
With a 128-bit cipher you instead get three keys (including the per-link one) and
mesh plink: ESTAB within about 70 seconds.
What it looks like from outside
Stations show up in the list and then vanish, over and over. That’s not partial success —
the radio hears its neighbour’s beacons and creates an entry, tries to peer, fails, deletes
the entry, then rediscovers it on the next beacon and repeats.
Throughout all of it: no per-link key, no batman neighbour, no traffic.
Where it actually fails
My first look at the logs was misleading. I saw SAE: Confirm mismatch and lots of
MESH-SAE-AUTH-FAILURE and assumed the password exchange was failing. It isn’t —
MESH-SAE-AUTH-FAILURE is just a 10-second timeout firing, and the short timeout was
causing retries to trip over each other.
I rebuilt with a longer timeout (60s instead of 10s, 10 retries instead of 3) and ran it
for 8 minutes. Much clearer picture:
| Log line | Count |
|---|---|
SAE completed successfully (added PMKSA cache entry) |
424 |
SAE: Confirm mismatch |
0 |
MESH-SAE-AUTH-FAILURE |
1 |
Mesh RSN: invalid peer nonce |
60 |
| Link established | 0 |
The password exchange (SAE) is completely fine — it succeeds 424 times. What fails is
the step after it: the peering handshake that exchanges keys. The peer keeps rejecting it
with “invalid peer nonce” and then closes the connection:
MPM: Received PLINK action 3 <- peer sends a Close
MPM: close reason=52
MPM: peer_lid mismatch: 0x0 != 0xccf7
MPM set <peer> from OPN_SNT into HOLDING
Also worth noting: the longer timeout didn’t help at all, so this isn’t a speed problem.
Things I checked in the source
From GitHub - MorseMicro/hostap · GitHub , in case any of these are relevant:
- The peering element carries the group key, and its size changes with the cipher —
16 bytes for 128-bit, 32 bytes for 256-bit (mesh_rsn.c:570-574and:787-794). That’s
the only part of the handshake whose size depends on the cipher, and it’s the same
element whose nonce check is failing. SAE-EXT-KEYcan’t be used on a mesh. This is the standard way to get longer keys.
It’s compiled in and in the shipped binary, butmesh.conly checks for plain
WPA_KEY_MGMT_SAE(bit 10), whileSAE_EXT_KEYis bit 26 — so choosing it just turns
mesh security off entirely.- Heads-up for anyone else trying this: using a stronger SAE group (
sae_groups=20,
P-384) does not give you longer keys on a mesh. Key length depends on the AKM, not the
group. I wasted time on this. - Minor: the peering element always claims CCMP as the cipher regardless of what’s
configured (mesh_rsn.c:592). Nothing reads that field, so it’s harmless today, but it’s
wrong on the wire.
I tried a lot of variations
All of these fail the same way:
- Changing the cipher on a running mesh
- Restarting wifi on both radios at once
- Using
sae_groups=20 - Cold-rebooting both radios straight into the 256-bit setting
- Using CCMP-256 instead of GCMP-256
- Rebuilding with much longer timeouts and retry counts
About newer releases
I’m on 1.16.4 and I know 1.17.8, 1.17.9, mm8108-2.0.0 and mm6108-2.0.1 have since
come out. I checked the mesh source at the mm8108-2.0.0 tag and it’s unchanged in every
place I looked — same hardcoded CCMP, same SAE_PMK_LEN, same timeouts, same SAE-EXT-KEY
lockout, still no 256-bit handling.
But I haven’t tested 2.0.0 on hardware yet. Since the group key installs fine and only the
per-link key fails, a driver or firmware fix would be in exactly the part I can’t inspect —
so if 2.0.0 already fixes this, please say so and I’ll go test it.
Questions
- Is GCMP-256 / CCMP-256 supposed to work on an 802.11s mesh with the MM8108? If not,
I’d rather know now and stop chasing it. - Can the silent downgrade in
morse_overrides.shbe fixed? Setting a 256-bit cipher and
silently getting CCMP-128 is an easy trap to fall into. - Any chance of enabling SAE-EXT-KEY for mesh mode? That’s the proper route to 256-bit
mesh keys. - Does the firmware handle a 32-byte per-link key at all? I could never get far enough to
find out. - Is there anything in the peering element handling that assumes a 16-byte group key?
Happy to run more tests, try patches, or send full debug logs.