The adoption process was stuck in a loop because device was using the wrong authentication key.
[build_payload] Payload state=1, default=true, adopted=0, x_authkey=c93aec16...
adopted=0✓ Correct (not yet adopted)x_authkey=c93aec16✗ WRONG! Should beba86f2bbe107c7c57eb5f2690775c712(DEFAULT_AUTH_KEY)
- Device encrypts inform packet with wrong key (c93aec16)
- Controller receives packet, tries to decrypt with DEFAULT key
- Decryption fails or controller rejects packet
- Controller never sends
set-adoptcommand - Device stuck in loop sending only
setparamresponses
The Bug:
// OLD CODE - WRONG!
if (!strcmp(key, "authkey")) {
LOG("Updating authkey from mgmt_cfg");
strncpy(st->authkey, val, sizeof(st->authkey)-1); ← Saves WRONG key!
}The code was extracting authkey from the mgmt_cfg string in setparam responses and saving it even though adopted=0.
Timeline of corruption:
Controller sends: setparam { mgmt_cfg: "authkey=c93aec16\nuse_aes_gcm=true\n..." }
↓
Old code: "there's an authkey in mgmt_cfg, save it!"
↓
Device saves: st.authkey=c93aec16, st.adopted=0 ← WRONG STATE!
↓
Next inform encrypts with c93aec16 instead of DEFAULT
↓
Controller receives packet encrypted with unknown key
↓
Controller never sends set-adopt command
↓
Stuck in loop forever
The Critical Mistake:
The mgmt_cfg.authkey field is a management configuration parameter (device config sent by controller), NOT the adoption key. These are different:
- DEFAULT_AUTH_KEY: Used for unadopted devices to start conversation
- mgmt_cfg.authkey: Management parameters for already-connected devices
- Adoption key (from set-adopt): The real key for encrypted communication after adoption
If a previous run saved the wrong authkey with adopted=0, the device would:
- Reboot/restart
- Load state:
adopted=0, authkey=c93aec16(from corrupted file) - Never recover unless state file is deleted
STEP 1: Device Starts Unadopted
├─ adopted = 0
├─ authkey = DEFAULT (ba86f2bbe107c7c57eb5f2690775c712)
└─ Send inform with x_authkey=DEFAULT, state=1, default=true
STEP 2: Controller Receives Unadopted Inform
└─ Recognizes DEFAULT key, decrypts payload, sees unadopted device
STEP 3: Controller Responds with Adoption Command
└─ Response: { _type: "cmd", cmd: "set-adopt", key: "c93aec16...", uri: "https://..." }
STEP 4: Device Processes Adoption
├─ Saves: adopted=1, authkey=c93aec16
└─ Saves state to file
STEP 5: Device Sends Next Inform as Adopted
├─ authkey = c93aec16 (new adoption key)
├─ state = 4
├─ default = false
└─ Encrypted with c93aec16
STEP 6: Controller Now Manages Device
└─ Sends setstate, radio_table, vap_table, etc.
Changes:
- ❌ Removed: Code that extracts and saves
authkeyfrommgmt_cfg - ✅ Kept: Code that extracts
cfgversion(needed for config management) - ✅ Added: Explanation comment about why authkey from mgmt_cfg is ignored
Code:
if (!strcmp(key, "cfgversion")) {
strncpy(st->cfgversion, val, sizeof(st->cfgversion)-1);
} else if (!strcmp(key, "mgmt_url")) {
/* Could save mgmt_url for future use */
}
/* IMPORTANT: Do NOT save authkey from mgmt_cfg!
Only accept authkey from set-adopt command when adopted. */Purpose: Prevent corrupted state from blocking adoption forever.
Change:
/* If not adopted, force DEFAULT_AUTH_KEY */
if (!st->adopted) {
LOG("Device not adopted - resetting authkey to DEFAULT");
strncpy(st->authkey, DEFAULT_AUTH_KEY, sizeof(st->authkey) - 1);
}Purpose: Double-safety to catch any remaining incorrect keys during inform.
Change:
/* When not adopted, ALWAYS use DEFAULT_AUTH_KEY */
if (!st->adopted && st->authkey[0] && strcmp(st->authkey, DEFAULT_AUTH_KEY) != 0) {
LOG("WARNING: Device not adopted but has custom authkey! Using DEFAULT instead!");
key_hex = DEFAULT_AUTH_KEY;
}- Log initial device state at startup
- Log authkey validation steps
- Show key mismatch warnings
- Track state transitions in detail
# Delete corrupted state
rm /etc/openuf/state.json
# Recompile with logging
make -f Makefile.standalone ENABLE_LOGGING=1
# Run daemon
/usr/sbin/openuf
# Watch logs in another terminal
tail -f /var/log/openuf.logExpected behavior:
[state_load] State file not found, using defaults
[state_load] State loaded: adopted=0, authkey=ba86f...
[main] Initial device state: adopted=0, authkey=ba86f...
[inform_send] Sending inform: adopted=0, authkey=ba86f...
[build_payload] x_authkey=ba86f2bbe107c7c57eb5f2690775c712 ← CORRECT!
[inform_send] HTTP POST to http://192.168.1.2:8080/inform, status: 200
[inform_send] Parsed response JSON: {..._type:cmd, cmd:set-adopt...}
[handle_response] Handling response type: cmd
[handle_response] Adopted successfully. Key: c93aec16...
[state_save] Saving state: adopted=1, authkey=c93aec16...
# Simulate corrupted state from logs
cat > /etc/openuf/state.json << 'EOF'
{
"adopted": false,
"authkey": "c93aec16f4e183539227b0656f0787a1",
"inform_url": "http://192.168.1.2:8080/inform",
"cfgversion": "0",
"mac": "80:af:ca:8f:d9:bb",
"ip": "192.168.1.5",
"hostname": "U6 IW"
}
EOF
# Run daemon
/usr/sbin/openuf
# Watch logs
tail -f /var/log/openuf.logExpected behavior:
[state_load] Device not adopted - resetting authkey to DEFAULT
[state_load] State loaded: adopted=0, authkey=ba86f...
[main] Initial device state: adopted=0, authkey=ba86f...
[inform_send] Sending inform: adopted=0, authkey=ba86f...
[build_payload] x_authkey=ba86f2bbe107c7c57eb5f2690775c712 ← FIXED!
Look for this in logs - should NOT appear:
❌ SHOULD NOT SEE: [handle_response] mgmt_cfg param: authkey =
- When unadopted: logs show
x_authkey=ba86f2bbe107...(DEFAULT) - Device transitions to adopted
- Once adopted: logs show
x_authkey=c93aec16...(adoption key) - No "Updating authkey from mgmt_cfg" logs
- No "WARNING: Device not adopted but has custom authkey" logs
- Controller UI shows device moving from "Adoption" → "Connected"
- Device survives restart while adopted with correct key
- Corrupted state file is always corrected on device startup
# Check current state on device
cat /etc/openuf/state.json
# Check if authkey is wrong while unadopted
grep "adopted.*:.*false" /etc/openuf/state.json && \
grep "authkey.*:.*c93aec16" /etc/openuf/state.json && \
echo "ERROR: Corrupted state found!"
# Watch adoption process
tail -f /var/log/openuf.log | grep -E "Sending inform|set-adopt|x_authkey"
# Reset device to unadopted state
rm /etc/openuf/state.json
/etc/init.d/openuf restartWhat was wrong:
- setparam handler saved wrong key from mgmt_cfg
- Device used c93aec16 instead of DEFAULT when unadopted
- Controller never sent adoption command
- Device stuck in configuration loop
How it's fixed:
- Don't extract/save authkey from mgmt_cfg (it's config, not auth)
- Force DEFAULT_AUTH_KEY when device is unadopted
- Only accept authkey during set-adopt command
- Corrupted state files are automatically corrected on load
- Double-safety check prevents encryption with wrong key
Result:
- Device always starts adoption with DEFAULT key
- Controller recognizes unadopted device
- Adoption command flows correctly
- Adoption completes successfully