]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
wifi-scripts: ucode: add WPA3-Personal Compatibility Mode
authorHauke Mehrtens <hauke@hauke-m.de>
Sun, 19 Apr 2026 19:38:56 +0000 (21:38 +0200)
committerHauke Mehrtens <hauke@hauke-m.de>
Sat, 2 May 2026 18:34:22 +0000 (20:34 +0200)
The WPA3 and Wi-Fi Enhanced Open Deployment and Implementation Guide
v1.1 ยง2.4 (Tables 6 and 7) defines WPA3-Personal Compatibility Mode:
the AP advertises a legacy-looking RSNE (WPA-PSK, CCMP-128, PMF
Disabled) while RSN Override Elements layered on top expose SAE and,
on EHT, SAE-EXT-KEY.  WPA2-only STAs and STAs that ignore RSN
Overriding associate unchanged; modern STAs pick up the stronger WPA3
AKM via RSNOE or RSNO2E.

Only the pairwise cipher differs between elements: RSNE and RSNOE
advertise CCMP-128, RSNO2E advertises GCMP-256 (EHT only).  Group
data (CCMP-128) and group management cipher (BIP-CMAC-128) are the
same in all three per Tables 6/7, so hostapd's BSS-wide group_cipher
and group_mgmt_cipher singletons produce the spec-correct values.

Unlike WPA3-Personal Transition Mode (sae-mixed), which puts PSK and
SAE together in the main RSNE with PMF Capable, Compatibility Mode
keeps the main RSNE strictly WPA2-shaped so clients that choke on a
mixed AKM list or PMF=Capable still see a pure WPA2 BSS.  The trade-
off is that clients without RSN Overriding support never pick up SAE.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Link: https://github.com/openwrt/openwrt/pull/23009
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/ap.uc
package/network/config/wifi-scripts/files-ucode/usr/share/ucode/wifi/iface.uc

index 633858d95d902bcd6b145605006b9a8a851108ca..dc8138d2ae8a6a07497b8b624366310e538d43a8 100644 (file)
@@ -81,14 +81,26 @@ function iface_accounting_server(config) {
        append_vars(config, [ 'radius_acct_req_attr' ]);
 }
 
-function iface_auth_type(config) {
+function iface_auth_type(config, band) {
        if (config.auth_type in [ 'sae', 'owe', 'eap2', 'eap192', 'dpp' ])
                config.ieee80211w = 2;
 
        if (config.auth_type in [ 'psk-sae', 'eap-eap2' ])
                set_default(config, 'ieee80211w', 1);
 
-       if (config.auth_type in [ 'sae', 'psk-sae' ]) {
+       if (config.auth_type == 'psk-sae-compat') {
+               if (band == '6g') {
+                       set_default(config, 'ieee80211w', 2);
+               } else {
+                       set_default(config, 'ieee80211w', 0);
+                       config.rsn_override_mfp = 2;
+                       config.rsn_override_omit_rsnxe = 1;
+               }
+               if (config.rsn_override_pairwise_2)
+                       config.rsn_override_mfp_2 = 2;
+       }
+
+       if (config.auth_type in [ 'sae', 'psk-sae', 'psk-sae-compat' ]) {
                config.sae_require_mfp = 1;
                if (!config.ppsk)
                        set_default(config, 'sae_pwe', 2);
@@ -122,6 +134,7 @@ function iface_auth_type(config) {
        case 'psk2':
        case 'sae':
        case 'psk-sae':
+       case 'psk-sae-compat':
                config.vlan_possible = 1;
                config.wps_possible = 1;
 
@@ -137,12 +150,12 @@ function iface_auth_type(config) {
                         netifd.setup_failed('INVALID_WPA_PSK');
                }
 
-               if (config.auth_type in [ 'psk', 'psk-sae' ]) {
+               if (config.auth_type in [ 'psk', 'psk-sae', 'psk-sae-compat' ] && band != '6g') {
                        set_default(config, 'wpa_psk_file', `/var/run/hostapd-${config.ifname}.psk`);
                        touch_file(config.wpa_psk_file);
                }
 
-               if (config.auth_type in [ 'sae', 'psk-sae' ]) {
+               if (config.auth_type in [ 'sae', 'psk-sae', 'psk-sae-compat' ]) {
                        set_default(config, 'sae_password_file', `/var/run/hostapd-${config.ifname}.sae`);
                        touch_file(config.sae_password_file);
                }
@@ -198,7 +211,7 @@ function iface_auth_type(config) {
 }
 
 function iface_ppsk(config) {
-       if (!(config.auth_type in [ 'none', 'owe', 'psk', 'sae', 'psk-sae', 'wep' ]) || !config.auth_server_addr)
+       if (!(config.auth_type in [ 'none', 'owe', 'psk', 'sae', 'psk-sae', 'psk-sae-compat', 'wep' ]) || !config.auth_server_addr)
                return;
 
        iface_authentication_server(config);
@@ -402,7 +415,9 @@ function iface_roaming(config) {
 }
 
 function iface_mfp(config) {
-       if (!config.ieee80211w || config.wpa < 2) {
+       let override_mfp = config.rsn_override_mfp || config.rsn_override_mfp_2;
+
+       if ((!config.ieee80211w && !override_mfp) || config.wpa < 2) {
                append('ieee80211w', 0);
                return;
        }
@@ -432,7 +447,7 @@ function iface_key_caching(config) {
                        'rsn_preauth', 'rsn_preauth_interfaces'
                ]);
        } else {
-               set_default(config, 'okc', (config.auth_type in  [ 'sae', 'psk-sae', 'owe' ]));
+               set_default(config, 'okc', (config.auth_type in  [ 'sae', 'psk-sae', 'psk-sae-compat', 'owe' ]));
        }
 
        if (!config.okc && !config.fils)
@@ -487,12 +502,12 @@ export function generate(interface, data, config, vlans, stas, phy_features) {
                        config.auth_type = 'eap2';
        }
 
-       if (config.auth_type in [ 'psk', 'psk-sae' ])
+       if (config.auth_type in [ 'psk', 'psk-sae', 'psk-sae-compat' ] && data.config.band != '6g')
                iface_wpa_stations(config, stas);
-       if (config.auth_type in [ 'sae', 'psk-sae' ])
+       if (config.auth_type in [ 'sae', 'psk-sae', 'psk-sae-compat' ])
                iface_sae_stations(config, stas);
 
-       iface_auth_type(config);
+       iface_auth_type(config, data.config.band);
 
        iface_accounting_server(config);
 
@@ -520,7 +535,7 @@ export function generate(interface, data, config, vlans, stas, phy_features) {
 
        iface_interworking(config);
 
-       iface.wpa_key_mgmt(config);
+       iface.wpa_key_mgmt(config, data.config.band);
        append_vars(config, [
                'wpa_key_mgmt',
        ]);
@@ -541,6 +556,10 @@ export function generate(interface, data, config, vlans, stas, phy_features) {
                ]);
        }
 
+       if (config.rsn_override_omit_rsnxe) {
+               append_vars(config, ['rsn_override_omit_rsnxe']);
+       }
+
        /* raw options */
        for (let raw in config.hostapd_bss_options)
                append_raw(raw);
index 1f139e3a34e09f123917d5a4ab06d9065c83e97a..0193eaeef1a1532c6cdfc7fa13a61a4408cd0923 100644 (file)
@@ -57,6 +57,15 @@ export function parse_encryption(config, dev_config) {
                config.auth_type = 'psk-sae';
                break;
 
+       case 'sae-compat':
+               config.auth_type = 'psk-sae-compat';
+               config.wpa_pairwise = 'CCMP';
+               if (dev_config.band != '6g')
+                       config.rsn_override_pairwise = 'CCMP';
+               if (wildcard(dev_config.htmode ?? '', 'EHT*'))
+                       config.rsn_override_pairwise_2 = 'GCMP-256';
+               break;
+
        case 'wpa':
        case 'wpa2':
        case 'wpa-mixed':
@@ -104,7 +113,7 @@ export function parse_encryption(config, dev_config) {
                config.wpa_pairwise ??= 'CCMP';
 };
 
-export function wpa_key_mgmt(config) {
+export function wpa_key_mgmt(config, band) {
        if (!config.wpa)
                return;
 
@@ -174,6 +183,36 @@ export function wpa_key_mgmt(config) {
                        append_value(config, 'wpa_key_mgmt', 'FT-PSK');
                break;
 
+       case 'psk-sae-compat':
+               if (band == '6g') {
+                       append_value(config, 'wpa_key_mgmt', 'SAE');
+                       if (config.ieee80211r)
+                               append_value(config, 'wpa_key_mgmt', 'FT-SAE');
+
+                       if (config.sae_ext_key && config.rsn_override_pairwise_2) {
+                               append_value(config, 'rsn_override_key_mgmt_2', 'SAE-EXT-KEY');
+                               if (config.ieee80211r)
+                                       append_value(config, 'rsn_override_key_mgmt_2', 'FT-SAE-EXT-KEY');
+                       }
+               } else {
+                       append_value(config, 'wpa_key_mgmt', 'WPA-PSK');
+                       if (config.ieee80211w)
+                               append_value(config, 'wpa_key_mgmt', 'WPA-PSK-SHA256');
+                       if (config.ieee80211r)
+                               append_value(config, 'wpa_key_mgmt', 'FT-PSK');
+
+                       append_value(config, 'rsn_override_key_mgmt', 'SAE');
+                       if (config.ieee80211r)
+                               append_value(config, 'rsn_override_key_mgmt', 'FT-SAE');
+
+                       if (config.sae_ext_key && config.rsn_override_pairwise_2) {
+                               append_value(config, 'rsn_override_key_mgmt_2', 'SAE-EXT-KEY');
+                               if (config.ieee80211r)
+                                       append_value(config, 'rsn_override_key_mgmt_2', 'FT-SAE-EXT-KEY');
+                       }
+               }
+               break;
+
        case 'owe':
                append_value(config, 'wpa_key_mgmt', 'OWE');
                break;