Compare commits

..

1 Commits

Author SHA1 Message Date
John Crispin
55691669ea hostapd: fix VLAN assignment lost on MPSK roaming between bands
When a client roams between bands (2.4/5/6 GHz) using RADIUS-based
Multi-PSK (MPSK), the VLAN assignment is lost and the client gets
placed on the default VLAN 1.

Root cause: On initial authentication, RADIUS provides both the PSK
and VLAN assignment. On subsequent roaming events, when using cached
credentials (PMK caching), RADIUS only validates the PSK without
re-sending the VLAN assignment, causing the client to fall back to
VLAN 1.

Solution: Store the VLAN ID in the cached PSK structure when first
assigned by RADIUS. On roaming, if RADIUS doesn't provide a VLAN,
restore it from the cached PSK entry.

The VLAN cache lifetime matches the PSK cache, automatically expiring
when the station disconnects.

Fixes: WIFI-14179
Signed-off-by: John Crispin <john@phrozen.org>
2025-10-02 07:33:11 +02:00
10 changed files with 89 additions and 30 deletions

View File

@@ -0,0 +1,55 @@
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -153,6 +153,7 @@ struct hostapd_sta_wpa_psk_short {
unsigned int is_passphrase:1;
u8 psk[PMK_LEN];
char passphrase[MAX_PASSPHRASE_LEN + 1];
+ int vlan_id;
int ref; /* (number of references held) - 1 */
};
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -2404,6 +2404,42 @@ int ieee802_11_set_radius_info(struct ho
vlan_id->tagged[0] ? "+" : "");
return -1;
}
+
+ /* Store VLAN ID with cached PSK for MPSK roaming support */
+ if (hapd->conf->ssid.dynamic_vlan && psk && vlan_id->notempty &&
+ vlan_id->untagged > 0) {
+ struct hostapd_sta_wpa_psk_short *psk_entry;
+
+ for (psk_entry = psk; psk_entry; psk_entry = psk_entry->next) {
+ psk_entry->vlan_id = vlan_id->untagged;
+ wpa_printf(MSG_DEBUG,
+ "RADIUS MPSK: Stored VLAN %d with PSK for "
+ MACSTR, vlan_id->untagged,
+ MAC2STR(sta->addr));
+ }
+ }
+
+ /* Restore VLAN from cached PSK if not provided by RADIUS */
+ if (hapd->conf->ssid.dynamic_vlan && !vlan_id->notempty && sta->psk) {
+ struct hostapd_sta_wpa_psk_short *psk_entry;
+ int cached_vlan = 0;
+
+ for (psk_entry = sta->psk; psk_entry; psk_entry = psk_entry->next) {
+ if (psk_entry->vlan_id > 0) {
+ cached_vlan = psk_entry->vlan_id;
+ break;
+ }
+ }
+
+ if (cached_vlan > 0) {
+ vlan_id->notempty = 1;
+ vlan_id->untagged = cached_vlan;
+ wpa_printf(MSG_DEBUG,
+ "RADIUS MPSK: Restored VLAN %d from cached PSK for " MACSTR,
+ cached_vlan, MAC2STR(sta->addr));
+ }
+ }
+
if (ap_sta_set_vlan(hapd, sta, vlan_id) < 0)
return -1;
if (sta->vlan_id)

View File

@@ -1180,7 +1180,7 @@ CONFIG_QTI_LICENSE_MANAGER=y
# CONFIG_SYMBOLIC_ERRNAME is not set
# CONFIG_LEDS_BRIGHTNESS_HW_CHANGED is not set
# CONFIG_LEDS_CLASS_MULTICOLOR is not set
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set
# CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY is not set
# CONFIG_PINCTRL_SINGLE is not set
# CONFIG_RANDOM_TRUST_BOOTLOADER is not set

View File

@@ -1,5 +1,5 @@
{
"major": 4,
"minor": 1,
"patch": 1
"patch": 0
}

View File

@@ -275,27 +275,23 @@ handlers = {
},
vlan_add: function(notify) {
if (config.config.swconfig)
return handlers.vlan_add_swconfig(notify);
let vlan_id = `${notify.data.vlan_id}`;
vlan_refcount[vlan_id] = (vlan_refcount[vlan_id] || 0) + 1;
let parts = split(notify.data.ifname, '-v');
let is_wifi_iface = (length(parts) == 2 && wildcard(parts[0], 'wlan*'));
if (vlan_refcount[vlan_id] > 1 && !is_wifi_iface)
if (vlan_refcount[vlan_id] > 1) {
return;
}
if (vlan_refcount[vlan_id] == 1) {
for (let wan in wan_ports) {
let msg = {
name: wan,
vlan: [ `${notify.data.vlan_id}:t` ]
};
ubus.call('network.interface.up_none', 'add_device', msg);
ubus.call('udevstats', 'add_device', { device: wan, vlan: +notify.data.vlan_id });
}
if (config.config.swconfig)
return handlers.vlan_add_swconfig(notify);
for (let wan in wan_ports) {
let msg = {
name: wan,
vlan: [ `${notify.data.vlan_id}:t` ]
};
ubus.call('network.interface.up_none', 'add_device', msg);
ubus.call('udevstats', 'add_device', { device: wan, vlan: +notify.data.vlan_id });
}
let msg = {
@@ -308,16 +304,16 @@ handlers = {
},
vlan_remove: function(notify) {
if (config.config.swconfig)
return;
let vlan_id = `${notify.data.vlan_id}`;
vlan_refcount[vlan_id] = (vlan_refcount[vlan_id] || 1) - 1;
if (vlan_refcount[vlan_id] > 0)
if (vlan_refcount[vlan_id] > 0) {
return;
}
delete vlan_refcount[vlan_id];
if (config.config.swconfig)
return;
for (let wan in wan_ports) {
let msg = {
name: wan,

View File

@@ -4,10 +4,10 @@ PKG_NAME:=ucentral-schema
PKG_RELEASE:=1
PKG_SOURCE_URL=https://github.com/Telecominfraproject/wlan-ucentral-schema.git
PKG_MIRROR_HASH:=c0f43db0530a38eb424e81908ad47a14e1d4d8f8a86eb148e34f98187c79ba6b
PKG_MIRROR_HASH:=f72d2e5b01ecb7a488d50d860da63664d992e50c7e046fed866be6733bab3c1c
PKG_SOURCE_PROTO:=git
PKG_SOURCE_DATE:=2025-10-16
PKG_SOURCE_VERSION:=dc9cad95641266a08de73aab85d931d992090159
PKG_SOURCE_DATE:=2025-09-29
PKG_SOURCE_VERSION:=676e1550c53b7d48a54aa759f65d341168627c5e
PKG_MAINTAINER:=John Crispin <john@phrozen.org>
PKG_LICENSE:=BSD-3-Clause

View File

@@ -1,12 +1,17 @@
{
"uuid": 2,
"radios": [
{
"band": "6G",
"country": "CA",
"channel-mode": "HE",
"channel-width": 80
},
{
"band": "5G",
"country": "CA",
"channel-mode": "HE",
"channel-width": 80,
"channel": 36
"channel-width": 80
},
{
"band": "2G",
@@ -52,7 +57,10 @@
"key": "bbbbbbbb"
}
],
"roaming": true
"roaming": {
"message-exchange": "ds",
"generate-psk": true
}
}
]
},

View File

@@ -17,4 +17,4 @@ packages:
- sysstat
- kmod-cig-poe-judgment
diffconfig: |
CONFIG_KERNEL_IPQ_MEM_PROFILE=0
CONFIG_KERNEL_IPQ_MEM_PROFILE=0

View File

@@ -16,4 +16,4 @@ packages:
- sysstat
- kmod-cig-poe-judgment
diffconfig: |
CONFIG_KERNEL_IPQ_MEM_PROFILE=0
CONFIG_KERNEL_IPQ_MEM_PROFILE=0