[wpa_supplicant] cumilative patch from commit 92374d59d
This merge is done mainly to support RSNE overriding for STA.
Bug: 348669010
Bug: 355076858
Test: Connect to open, WPA2, WPA3 and OWE
Test: Establish P2P connection
Test: Basic SoftAp tests
Test: Ran above tests on Pixel6
Test: Regression test (b/355078233)
BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from open source
92374d59d Enhance select_network() to trigger new scans in some cases
ff99012d8 RSNO: Use correct MLO capability while fetching RSNE/RSNXE
526ea193c Fallback to RSNXE when AP is not using valid RSN Overrding
4417b5ba8 Add QCA vendor interface to support Unsynchronized Service Discovery
765c48d5a RSNE/RSNXE overriding for STA
d0b55eb36 Make driver capabilities for AKM suites available within wpa_supplicant
5488e120d Use helper functions to access RSNE/RSNXE from BSS entries
341bcb2b5 nl80211: Add a capability flag for RSN overriding
6fad7224b Add QCA vendor feature flags to indicate RSN override elements support
157b01638 RSNE/RSNXE overriding for AP
b8a2d11ae Allow RSNXE Override element to override RSNXE contents during parsing
48ca68f6f Allow RSNE Override element to override RSNE contents during parsing
c16ac89be Add RSN overriding elements into IE parsing
6b0ce29d2 Define WFA vendor specific element types for RSNE/RSNXE overriding
e99cdfae4 The main branch is now used for v2.12 development
d945ddd36 Preparations for v2.11 release
aa2dfae5e dbus: Fix memory leak with Bonjour params for a P2P UPnP service
70e5bad56 dbus: Fix SignalChange property
ed5887a8c Move NULL check for driver private data (drv_priv)
c3d305d93 FT: Fix FTE MIC calculation with fragmented FTE
e7172e26d MLD STA: Find partner links by BSSID and SSID
816e22bba hostapd: Fix opclass during CSA with DFS channels
93a3c59ad Multi-AP: Honor wds_sta even with multi_ap
7d51bf2ab SAE: Drop default dot11RSNASAESync value from 5 to 3
5da59ff1c SAE: Disable protocol instance temporarily on sync error in mesh
7b8517d19 nl80211: Fix AP scan with STA fallback error path
6ad59779c nl80211: NAN: Register multicast action frames if possible
92829d8be NAN: Reject undefined publish type
44f20382c NAN: Fix a typo in USD doc
7c935eef3 nl80211: AP MLD: Reassign drv->ctx correctly to prevent hostapd crash
102365453 Make Beacon frame checks less frequent for SSID verification
fcf799c0d wpa_supplicant: Do not select a rejected SAE group
5f83f4db0 Add int_array_includes()
094e188f8 wpa_supplicant: Always clear SAE rejected groups on roaming to another BSS
627b67f29 ACS: Fix primary channel puncturing in ACS
be2ac9291 ACS: Fix ACS behavior for channel selection
83cfeb890 ACS: Update ACS documentation
cb91ef256 MLO: Swap Tx/Rx keys for GTK TKIP Michael MIC in MLO GTK KDE
7d314d6bd Fix channel switch without 'ht' for HE and EHT modes in 2.4 GHz band
99e82880e Fix mesh 6 GHz incorrect channel bandwidth
5452a4a30 SSID verification based on beacon protection
89b164138 BSS: Add wpa_bss_get_ie_beacon()
7436b5b01 Indicate if BIGTK has been set in STATUS output
c6f394b88 Indicate if SSID has been verified in STATUS output
b745cd33e PASN: Derive KDK on AP only when both ends support SecureLTF
e5f76b915 dbus: Fix error path in scan request handling
58b275955 trace: Only permit explicit prefix matching for functions
49344db09 trace: Use strncmp() to match function names
ac15b79fe PMKSA: Guard against NULL KCK for memcpy()
7bcede06e MLD: Ensure link_bssid array has space for sentinel
cf3883f3d MLD: Ensure link BSSIDs remain on stack for ignore
9f0429c9e dbus: Make sure ServiceDiscoveryRequest/Result does not override pointers
d22401d89 dbus: Fix memory leak in case dbus provides 'tlvs' in invalid P2P SD response
0c2d8417c dbus: Fix memory leak in case dbus provides tlv in P2P UPnP SD request
3b4f12708 nl80211: Use actual number of supported AKMs for AP setup
8f69e538a SecureLTF: Work around misbehaving STAs for PTK derivation without KDK
438a27b36 Do not derive SAE PT if the network profile does not include SAE
61eb89d5f nl80211: AP MLD: Parse link ID to determine the BSS for color event
5d16ad9ab nl80211: Refactor color collision related nl80211 commands handling
22a592d11 hostapd: Fix updating Beacon frames during association handling
9716bf116 SAE: Reject invalid Rejected Groups element in the parser
593a7c2f8 SAE: Check for invalid Rejected Groups element length explicitly on STA
5f98c853e nl80211: Send link ID with NL80211_CMD_TDLS_MGMT to enable TDLS with MLO
f302d9f96 RADIUS: Check Message-Authenticator if it is present even if not required
58097123e RADIUS: Require Message-Authenticator attribute in MAC ACL cases
934b0c3a4 Require Message-Authenticator in Access-Reject even without EAP-Message
f54157077 RADIUS DAS: Move Message-Authenticator attribute to be the first one
37fe8e48a hostapd: Move Message-Authenticator attribute to be the first one in req
689a24826 eapol_test: Move Message-Authenticator attribute to be the first one
54abb0d3c RADIUS server: Place Message-Authenticator attribute as the first one
adac846bd RADIUS: Allow Message-Authenticator attribute as the first attribute
d944ef1c0 SAE: Clear rejected groups list on completing authentication
0ab009db3 SAE: Clear rejected groups list on continuous failures
21fe04281 SAE: Clear peer_rejected_groups when no element is included
364c2da87 SAE: Check for invalid Rejected Groups element length explicitly
c9db4925f Vendor attribute to configure STA to follow AP preference for candidates
0cb42655f Vendor command extension for Responder PM Mode bit in TWT SET Request
9832f1324 Add vendor flag to indicate unavailability mode in TWT responder mode
761041b18 SAE: Free password identifier if SAE commit is rejected due to it
d97b5c649 Define Link Id attribute for secure ranging context vendor command
2097de2a6 Define Link Id attribute for QCA_NL80211_VENDOR_SUBCMD_PASN
c6e55fb96 Add Link ID for External ACS vendor command
37a289f8b SSID protection in 4-way handshake on AP
dab7549d6 SSID protection in 4-way handshake on STA
9a022cdc7 STA: Update scan results when BSS entry with current SSID is not found
320c4c8f8 AP MLD: Send link id to the driver during color change
ecfe2aa61 Update Beacon frames after color change
5913d1a18 Remove double "on" from debug prints in CCA event callbacks
d8e1a353a hostapd: Add support to change BSS color from the control interface
3e52a90d3 ACS: Handle scan start request failure with error code -EBUSY
3cf7bf68f AP MLD: Fix deferred first link BSS's authentication server init
2829f1c43 wlantest: Initial support for Multiple BSSID procedure
1b96745f1 Add a new QCA vendor attribute to set interface offload type
ffcb7392f Add vendor attributes to detect data stall for consecutive TX no ack
a5ee11e02 Add new traffic type values for flow report vendor attribute
2c89b56d6 WNM: Include BSS max idle period in STATUS command output
58ac46baf WNM: AP configuration to allow BSS max idle period requests
6594ea9ef WNM: Allow a specific BSS max idle period to be requested
6cd023111 WNM: Group rekeying skipping with BSS max idle period management
846b1d618 WNM: Configurable BSS Max Idle Period management on AP
7566370a9 Add QCA vendor attribute to get number of TX/RX packets for each NSS
4c0ea8270 Add vendor attributes to configure TX/RX NSS and chains per band
c484a0fca Add kernel documentation for nss and chain configuration vendor command
cb40986a7 Add QCA vendor attribute for uplink delay jitter
ed56dfc33 P2P: Fix fast IP address allocation for invitation of a persistent group
0ae087994 Add a new QCA vendor attribute to set reduced power scan mode
bd36dc90f AP MLD: Remove unused get_ml_rsn_info callback definition
51b5b9512 Update Probe Response template on BSS color change
6f1fbebeb Update Probe Response template on channel switch
7d0c08910 More generic unsolicited broadcast Probe Response template setup
195cc3d91 Make selection of current opclass more generic for 20 MHz UNI-III channels
b9113105a FILS: Add Operating Class and Primary Channel in FD for non-PSC chan
5929b4eb1 Define QCA vendor commands for flow stats/classification
5308029f8 nl80211: Update link bandwidth when receiving channel switch event
11dfdf64c AP MLD: Set link_id field in hostapd_freq_params when setting up AP
df14f1e2b Add QCA vendor subcommand to suspend/resume AP interface
df5988004 AP MLD: Add MLO Link KDE for each affiliated link in EAPOL-Key 3/4
b26971774 AP MLD: Do not store per-supplicant AP RSNE/RSNXE information
20872d525 AP MLD: Do not store per-supplicant AP link MAC address information
3b68eef7d AP MLD: Do not store per-supplicant AP MLD MAC address information
ed78f56dc Add a vendor attribute value to set aggressive roaming mode
e6ec62aa2 Allow Session-Timeout with PSK RADIUS during 4-way handshake
f44a07d5c wpa_cli: Make WPA_EVENT_CHANNEL_SWITCH events accessible to action scripts
2e1f7d091 Fix center segment indexes in channel switch fallback to non-5 GHz cases
7cf3ceada P2P: Call normal SD query callback on RX/TX race
9b1e0ab4e dbus: Use correct values for persistent group
cf36ffd43 wpa_supplicant: Do not allow fast associate before scanning 6 GHz
ff798fbb8 ctrl_iface: Allow sending ML probe without AP MLD ID
4d2f76fab MLD: Use AP MLD MAC address with deauthenticate
5d9b4a1a1 SME: MLD: Clear MLD state only after the deauthentication
39fefeada SME: MLD: Deauthenticate when failing to parse ML element
6f3e7c5d3 wpa_supplicant: Do not roam to an associated link
2bbe4822a Clear connect_without_scan on network profile removal
b3ad54e46 Check whether to skip a BSS in RNR with a shared helper
de1bfda64 Fix RNR building for co-location and MLO
8d434bf65 AP MLD: Add link details in STATUS command
b1e463374 AP MLD: Link-specific flushing of stations
5e3c2b489 AP MLD: Run authenticator state machine for all links
12acda633 AP MLD: Support group rekeying for MLO
62a8f96e5 AP MLD: Calculate ML KDE length separately for each link
78adbf2c0 AP MLD: Mark GKeyDone completed for STAs in a helper function
e5b49876a AP MLD: Debug print of MLO KDE lengths
84d2a36da AP MLD: Require same AKM and pairwise cipher for all links
8891ebdc1 Use defined values for RSN PN length
3ea7cf11d AP MLD: Enhance authenticator state machine
19fdcf511 AP MLD: Skip association link processing in ML info
4a1197acd AP MLD: Update all partner links' beacons
a51881032 AP MLD: Handle link_id in EAPOL RX handler
eea52c4b5 AP MLD: Handle link_id in EAPOL TX status handler
636530bc2 hostapd: Make hostapd_eapol_tx_status() function static
93d204b1e nl80211: Move control port TX status to per BSS handling
efb484bbc nl80211: Move Management frame TX status to per BSS handling
80864d011 AP MLD/nl80211: Pass ctx in mlme_event_mgmt()
c36ad1150 AP MLD: Use link_id in the get_hapd_bssid() helper function
d9c5d601f AP NLD: Extend support for cohosted ML BSS
3d0cc612f AP MLD: Support cohosted ML BSS
9098535ef AP MLD: Reset authenticator state machine's ML info
866ed6324 Remove the bssid argument from send_auth_reply()
fd1a35e14 AP MLD: Handle authentication and association on link address
e4e772456 AP MLD: Use if/else/endif comments more consistently
9fcc636da nl80211: Restore libnl3-route inclusion for full VLAN support with netlink
61c8cc94f Add a vendor attribute to configure custom keep-alive interval for STA
47d1307d2 Add QCA vendor interface for reporting station info in unicast event
3c79173c3 Add TWT responder support for AP in HT and VHT modes
54b1df85c Add QCA vendor feature flag for TWT responder support in HT and VHT modes
85ea5f349 nl80211: Send link_id on sta_deauth()
62e0c1019 nl80211: Print the interface name in debug during link add
e8764518b nl80211: Generate link add command on per-BSS basis for AP MLD
16aea07e5 AP MLD: Simplify for_each_mld_link() macro
d43eb71da hostapd: Add support for testing Probe Response frame elements
Change-Id: I445ef0fed93ec7d4443ed33751318a46899e61e7
Signed-off-by: Sunil Ravi <sunilravi@google.com>
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 28b0ba7..f5b36d3 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -12,6 +12,7 @@
#include "utils/common.h"
#include "utils/list.h"
+#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/hw_features_common.h"
#include "common/wpa_ctrl.h"
@@ -75,7 +76,7 @@
*
* This corresponds to:
* ---
- * (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf)
+ * (busy time - tx time) / (active time - tx time) * 2^(chan_nf - band_min_nf)
* ---
*
* The coefficient of 2 reflects the way power in "far-field"
@@ -92,7 +93,7 @@
* calculated easily.
* ---
* (busy time - tx time) / (active time - tx time) *
- * 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * 2^(10^(chan_nf/10) - 10^(band_min_nf/10))
* ---
*
* However to account for cases where busy/rx time is 0 (channel load is then
@@ -100,7 +101,7 @@
* channel with lower noise floor is preferred. The equation becomes:
* ---
* 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) *
- * 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * 2^(10^(chan_nf/10) - 10^(band_min_nf/10))
* ---
*
* All this "interference factor" is purely subjective and only time
@@ -307,6 +308,7 @@
static int acs_request_scan(struct hostapd_iface *iface);
static int acs_survey_is_sufficient(struct freq_survey *survey);
+static void acs_scan_retry(void *eloop_data, void *user_data);
static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
@@ -352,6 +354,8 @@
iface->chans_surveyed = 0;
iface->acs_num_completed_scans = 0;
+ iface->acs_num_retries = 0;
+ eloop_cancel_timeout(acs_scan_retry, iface, NULL);
}
@@ -507,7 +511,7 @@
}
if (ret == -1)
- ret = 1; /* no survey list entries */
+ ret = 0; /* no survey list entries */
if (!ret) {
wpa_printf(MSG_INFO,
@@ -830,6 +834,9 @@
int bw320_offset = 0, ideal_bw320_offset = 0;
unsigned int k;
int secondary_channel = 1, freq_offset;
+#ifdef CONFIG_IEEE80211BE
+ int index_primary = 0;
+#endif /* CONFIG_IEEE80211BE */
if (is_24ghz_mode(mode->mode))
secondary_channel = iface->conf->secondary_channel;
@@ -969,6 +976,9 @@
best->chan, chan->chan,
chan->interference_factor,
best->interference_factor);
+#ifdef CONFIG_IEEE80211BE
+ index_primary = (chan->freq - best->freq) / 20;
+#endif /* CONFIG_IEEE80211BE */
chan = best;
}
@@ -1057,7 +1067,8 @@
if (iface->conf->ieee80211be)
acs_update_puncturing_bitmap(iface, mode, bw,
n_chans, chan,
- factor, 0);
+ factor,
+ index_primary);
#endif /* CONFIG_IEEE80211BE */
}
@@ -1317,6 +1328,7 @@
int err;
iface->scan_cb = NULL;
+ iface->acs_num_retries = 0;
wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)",
iface->conf->acs_num_scans);
@@ -1329,7 +1341,7 @@
if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
err = acs_request_scan(iface);
- if (err) {
+ if (err && err != -EBUSY) {
wpa_printf(MSG_ERROR, "ACS: Failed to request scan");
goto fail;
}
@@ -1382,7 +1394,7 @@
static int acs_request_scan(struct hostapd_iface *iface)
{
struct wpa_driver_scan_params params;
- int i, *freq;
+ int i, *freq, ret;
int num_channels;
struct hostapd_hw_modes *mode;
@@ -1415,24 +1427,62 @@
return -1;
}
- iface->scan_cb = acs_scan_complete;
+ if (!iface->acs_num_retries)
+ wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
+ iface->acs_num_completed_scans + 1,
+ iface->conf->acs_num_scans);
+ else
+ wpa_printf(MSG_DEBUG,
+ "ACS: Re-try scanning attempt %d (%d / %d)",
+ iface->acs_num_retries,
+ iface->acs_num_completed_scans + 1,
+ iface->conf->acs_num_scans);
- wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
- iface->acs_num_completed_scans + 1,
- iface->conf->acs_num_scans);
+ ret = hostapd_driver_scan(iface->bss[0], ¶ms);
+ os_free(params.freqs);
- if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) {
+ if (ret == -EBUSY) {
+ iface->acs_num_retries++;
+ if (iface->acs_num_retries >= ACS_SCAN_RETRY_MAX_COUNT) {
+ wpa_printf(MSG_ERROR,
+ "ACS: Failed to request initial scan (all re-attempts failed)");
+ acs_fail(iface);
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO,
+ "Failed to request acs scan ret=%d (%s) - try to scan after %d seconds",
+ ret, strerror(-ret), ACS_SCAN_RETRY_INTERVAL);
+ eloop_cancel_timeout(acs_scan_retry, iface, NULL);
+ eloop_register_timeout(ACS_SCAN_RETRY_INTERVAL, 0,
+ acs_scan_retry, iface, NULL);
+ return 0;
+ }
+
+ if (ret < 0) {
wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
acs_cleanup(iface);
- os_free(params.freqs);
return -1;
}
- os_free(params.freqs);
+ iface->scan_cb = acs_scan_complete;
+
return 0;
}
+static void acs_scan_retry(void *eloop_data, void *user_data)
+{
+ struct hostapd_iface *iface = eloop_data;
+
+ if (acs_request_scan(iface)) {
+ wpa_printf(MSG_ERROR,
+ "ACS: Failed to request re-try of initial scan");
+ acs_fail(iface);
+ }
+}
+
+
enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
{
int err;
diff --git a/src/ap/acs.h b/src/ap/acs.h
index ec84f0e..8be3de5 100644
--- a/src/ap/acs.h
+++ b/src/ap/acs.h
@@ -15,6 +15,9 @@
enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
void acs_cleanup(struct hostapd_iface *iface);
+#define ACS_SCAN_RETRY_MAX_COUNT 15
+#define ACS_SCAN_RETRY_INTERVAL 5
+
#else /* CONFIG_ACS */
static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 9aa61fa..a5a821b 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -92,6 +92,7 @@
bss->eap_sim_id = 3;
bss->eap_sim_aka_fast_reauth_limit = 1000;
bss->ap_max_inactivity = AP_MAX_INACTIVITY;
+ bss->bss_max_idle = 1;
bss->eapol_version = EAPOL_VERSION;
bss->max_listen_interval = 65535;
@@ -121,9 +122,10 @@
#endif /* CONFIG_IEEE80211R_AP */
bss->radius_das_time_window = 300;
+ bss->radius_require_message_authenticator = 1;
bss->anti_clogging_threshold = 5;
- bss->sae_sync = 5;
+ bss->sae_sync = 3;
bss->gas_frag_limit = 1400;
@@ -494,10 +496,14 @@
if ((conf->sae_pwe == SAE_PWE_HUNT_AND_PECK &&
!hostapd_sae_pw_id_in_use(conf) &&
- !wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt) &&
+ !wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt |
+ conf->rsn_override_key_mgmt |
+ conf->rsn_override_key_mgmt_2) &&
!hostapd_sae_pk_in_use(conf)) ||
conf->sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK ||
- !wpa_key_mgmt_sae(conf->wpa_key_mgmt))
+ !wpa_key_mgmt_sae(conf->wpa_key_mgmt |
+ conf->rsn_override_key_mgmt |
+ conf->rsn_override_key_mgmt_2))
return 0; /* PT not needed */
sae_deinit_pt(ssid->pt);
@@ -969,6 +975,7 @@
wpabuf_free(conf->igtk_rsc_override);
wpabuf_free(conf->eapol_m1_elements);
wpabuf_free(conf->eapol_m3_elements);
+ wpabuf_free(conf->presp_elements);
#endif /* CONFIG_TESTING_OPTIONS */
os_free(conf->no_probe_resp_if_seen_on);
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 4f2164d..1a4c912 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -309,6 +309,7 @@
struct hostapd_ip_addr own_ip_addr;
char *nas_identifier;
struct hostapd_radius_servers *radius;
+ int radius_require_message_authenticator;
int acct_interim_interval;
int radius_request_cui;
struct hostapd_radius_attr *radius_auth_req_attr;
@@ -357,7 +358,11 @@
int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
int extended_key_id;
int wpa_key_mgmt;
+ int rsn_override_key_mgmt;
+ int rsn_override_key_mgmt_2;
enum mfp_options ieee80211w;
+ enum mfp_options rsn_override_mfp;
+ enum mfp_options rsn_override_mfp_2;
int group_mgmt_cipher;
int beacon_prot;
/* dot11AssociationSAQueryMaximumTimeout (in TUs) */
@@ -386,6 +391,8 @@
u32 wpa_pairwise_update_count;
int wpa_disable_eapol_key_retries;
int rsn_pairwise;
+ int rsn_override_pairwise;
+ int rsn_override_pairwise_2;
int rsn_preauth;
char *rsn_preauth_interfaces;
@@ -465,6 +472,9 @@
*/
int ap_max_inactivity;
+ int bss_max_idle;
+ int max_acceptable_idle_period;
+ bool no_disconnect_on_group_keyerror;
int ignore_broadcast_ssid;
int no_probe_resp_if_max_sta;
@@ -709,6 +719,7 @@
struct wpabuf *eapol_m3_elements;
bool eapol_m3_no_encrypt;
int test_assoc_comeback_type;
+ struct wpabuf *presp_elements;
#ifdef CONFIG_IEEE80211BE
u16 eht_oper_puncturing_override;
@@ -956,6 +967,8 @@
char *config_id;
bool xrates_supported;
+ bool ssid_protection;
+
#ifdef CONFIG_IEEE80211BE
/* The AP is part of an AP MLD */
u8 mld_ap;
@@ -1227,6 +1240,9 @@
MBSSID_ENABLED = 1,
ENHANCED_MBSSID_ENABLED = 2,
} mbssid;
+
+ /* Whether to enable TWT responder in HT and VHT modes */
+ bool ht_vht_twt_responder;
};
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 11fe39c..c473491 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -208,6 +208,9 @@
add_buf(&beacon, hapd->conf->vendor_elements);
add_buf(&proberesp, hapd->conf->vendor_elements);
+#ifdef CONFIG_TESTING_OPTIONS
+ add_buf(&proberesp, hapd->conf->presp_elements);
+#endif /* CONFIG_TESTING_OPTIONS */
add_buf(&assocresp, hapd->conf->assocresp_elements);
*beacon_ret = beacon;
@@ -624,9 +627,17 @@
int hostapd_flush(struct hostapd_data *hapd)
{
+ int link_id = -1;
+
if (hapd->driver == NULL || hapd->driver->flush == NULL)
return 0;
- return hapd->driver->flush(hapd->drv_priv);
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf && hapd->conf->mld_ap)
+ link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ return hapd->driver->flush(hapd->drv_priv, link_id);
}
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index 6ed4d06..837b690 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -260,11 +260,20 @@
if (!hostapd_mld_is_first_bss(hapd)) {
struct hostapd_data *first;
- wpa_printf(MSG_DEBUG, "MLD: Using auth_serv of the first BSS");
-
first = hostapd_mld_get_first_bss(hapd);
if (!first)
return -1;
+
+ if (!first->eap_cfg) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: First BSS auth_serv does not exist. Init on its behalf");
+
+ if (authsrv_init(first))
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "MLD: Using auth_serv of the first BSS");
+
#ifdef EAP_TLS_FUNCS
hapd->ssl_ctx = first->ssl_ctx;
#endif /* EAP_TLS_FUNCS */
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 32865f6..f8ce810 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -403,6 +403,81 @@
}
+static u8 * hostapd_get_rsne_override(struct hostapd_data *hapd, u8 *pos,
+ size_t len)
+{
+ const u8 *ie;
+
+ ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_IE_VENDOR_TYPE);
+ if (!ie || 2U + ie[1] > len)
+ return pos;
+
+ os_memcpy(pos, ie, 2 + ie[1]);
+ return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_get_rsne_override_2(struct hostapd_data *hapd, u8 *pos,
+ size_t len)
+{
+ const u8 *ie;
+
+ ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_2_IE_VENDOR_TYPE);
+ if (!ie || 2U + ie[1] > len)
+ return pos;
+
+ os_memcpy(pos, ie, 2 + ie[1]);
+ return pos + 2 + ie[1];
+}
+
+
+static u8 * hostapd_get_rsnxe_override(struct hostapd_data *hapd, u8 *pos,
+ size_t len)
+{
+ const u8 *ie;
+
+ ie = hostapd_vendor_wpa_ie(hapd, RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+ if (!ie || 2U + ie[1] > len)
+ return pos;
+
+ os_memcpy(pos, ie, 2 + ie[1]);
+ return pos + 2 + ie[1];
+}
+
+
+static size_t hostapd_get_rsne_override_len(struct hostapd_data *hapd)
+{
+ const u8 *ie;
+
+ ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_IE_VENDOR_TYPE);
+ if (!ie)
+ return 0;
+ return 2 + ie[1];
+}
+
+
+static size_t hostapd_get_rsne_override_2_len(struct hostapd_data *hapd)
+{
+ const u8 *ie;
+
+ ie = hostapd_vendor_wpa_ie(hapd, RSNE_OVERRIDE_2_IE_VENDOR_TYPE);
+ if (!ie)
+ return 0;
+ return 2 + ie[1];
+}
+
+
+static size_t hostapd_get_rsnxe_override_len(struct hostapd_data *hapd)
+{
+ const u8 *ie;
+
+ ie = hostapd_vendor_wpa_ie(hapd, RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+ if (!ie)
+ return 0;
+ return 2 + ie[1];
+}
+
+
static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
{
#ifdef CONFIG_TESTING_OPTIONS
@@ -649,6 +724,10 @@
#endif /* CONFIG_FST */
if (hapd->conf->vendor_elements)
buflen += wpabuf_len(hapd->conf->vendor_elements);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->presp_elements)
+ buflen += wpabuf_len(hapd->conf->presp_elements);
+#endif /* CONFIG_TESTING_OPTIONS */
if (hapd->conf->vendor_vht) {
buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
2 + sizeof(struct ieee80211_vht_operation);
@@ -677,10 +756,14 @@
params->known_bss,
params->known_bss_len, NULL);
if (!params->is_ml_sta_info)
- buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
+ buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP,
+ true);
buflen += hostapd_mbo_ie_len(hapd);
buflen += hostapd_eid_owe_trans_len(hapd);
buflen += hostapd_eid_dpp_cc_len(hapd);
+ buflen += hostapd_get_rsne_override_len(hapd);
+ buflen += hostapd_get_rsne_override_2_len(hapd);
+ buflen += hostapd_get_rsnxe_override_len(hapd);
return buflen;
}
@@ -797,7 +880,8 @@
pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
if (!params->is_ml_sta_info)
- pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP);
+ pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP,
+ true);
pos = hostapd_eid_fils_indic(hapd, pos, 0);
pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
@@ -879,12 +963,24 @@
pos = hostapd_eid_owe_trans(hapd, pos, epos - pos);
pos = hostapd_eid_dpp_cc(hapd, pos, epos - pos);
+ pos = hostapd_get_rsne_override(hapd, pos, epos - pos);
+ pos = hostapd_get_rsne_override_2(hapd, pos, epos - pos);
+ pos = hostapd_get_rsnxe_override(hapd, pos, epos - pos);
+
if (hapd->conf->vendor_elements) {
os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
wpabuf_len(hapd->conf->vendor_elements));
pos += wpabuf_len(hapd->conf->vendor_elements);
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->presp_elements) {
+ os_memcpy(pos, wpabuf_head(hapd->conf->presp_elements),
+ wpabuf_len(hapd->conf->presp_elements));
+ pos += wpabuf_len(hapd->conf->presp_elements);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
return pos;
}
@@ -945,7 +1041,6 @@
{
struct probe_resp_params sta_info_params;
struct hostapd_data *link;
- unsigned int probed_mld_id, i, j;
params->mld_ap = NULL;
params->mld_info = os_zalloc(sizeof(*params->mld_info));
@@ -956,14 +1051,7 @@
"MLD: Got ML probe request with AP MLD ID %d for links %04x",
mld_id, links);
- /*
- * We want to include the AP MLD ID in the response if it was
- * included in the request.
- */
- probed_mld_id = mld_id != -1 ? mld_id : hostapd_get_mld_id(hapd);
-
- for_each_mld_link(link, i, j, hapd->iface->interfaces,
- probed_mld_id) {
+ for_each_mld_link(link, hapd) {
struct mld_link_info *link_info;
size_t buflen;
u8 mld_link_id = link->mld_link_id;
@@ -1706,15 +1794,15 @@
#ifdef CONFIG_IEEE80211AX
/* Unsolicited broadcast Probe Response transmission, 6 GHz only */
-static u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
- struct wpa_driver_ap_params *params)
+u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
+ struct unsol_bcast_probe_resp *ubpr)
{
struct probe_resp_params probe_params;
if (!is_6ghz_op_class(hapd->iconf->op_class))
return NULL;
- params->unsol_bcast_probe_resp_interval =
+ ubpr->unsol_bcast_probe_resp_interval =
hapd->conf->unsol_bcast_probe_resp_interval;
os_memset(&probe_params, 0, sizeof(probe_params));
@@ -1727,7 +1815,7 @@
probe_params.mld_info = NULL;
hostapd_gen_probe_resp(hapd, &probe_params);
- params->unsol_bcast_probe_resp_tmpl_len = probe_params.resp_len;
+ ubpr->unsol_bcast_probe_resp_tmpl_len = probe_params.resp_len;
return (u8 *) probe_params.resp;
}
#endif /* CONFIG_IEEE80211AX */
@@ -1942,6 +2030,17 @@
FD_FRAME_CTL_CAP_PRESENT;
total_len += 4 + 1 + 2;
+ /* Fill primary channel information for 6 GHz channels with over 20 MHz
+ * bandwidth, if the primary channel is not a PSC */
+ if (is_6ghz_op_class(hapd->iconf->op_class) &&
+ !is_6ghz_psc_frequency(ieee80211_chan_to_freq(
+ NULL, hapd->iconf->op_class,
+ hapd->iconf->channel)) &&
+ op_class_to_bandwidth(hapd->iconf->op_class) > 20) {
+ ctl |= FD_FRAME_CTL_PRI_CHAN_PRESENT;
+ total_len += 2;
+ }
+
/* Check for optional subfields and calculate length */
if (wpa_auth_write_fd_rsn_info(hapd->wpa_auth, fd_rsn_info)) {
ctl |= FD_FRAME_CTL_RSN_INFO_PRESENT;
@@ -1954,7 +2053,7 @@
total_len += 3;
}
- total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION);
+ total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION, true);
pos = hostapd_eid_fils_indic(hapd, buf, 0);
buf_len = pos - buf;
@@ -2003,9 +2102,11 @@
WPA_PUT_LE16(pos, hostapd_fils_discovery_cap(hapd));
pos += 2;
- /* Operating Class - not present */
-
- /* Primary Channel - not present */
+ /* Operating Class and Primary Channel - if a 6 GHz chan is non PSC */
+ if (ctl & FD_FRAME_CTL_PRI_CHAN_PRESENT) {
+ *pos++ = hapd->iconf->op_class;
+ *pos++ = hapd->iconf->channel;
+ }
/* AP Configuration Sequence Number - not present */
@@ -2028,7 +2129,7 @@
/* Fill in the Length field value */
*length_pos = pos - (length_pos + 1);
- pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION);
+ pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION, true);
/* FILS Indication element */
if (buf_len) {
@@ -2134,10 +2235,13 @@
if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED &&
hapd == hostapd_mbssid_get_tx_bss(hapd))
tail_len += 5; /* Multiple BSSID Configuration element */
- tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
+ tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON, true);
tail_len += hostapd_mbo_ie_len(hapd);
tail_len += hostapd_eid_owe_trans_len(hapd);
tail_len += hostapd_eid_dpp_cc_len(hapd);
+ tail_len += hostapd_get_rsne_override_len(hapd);
+ tail_len += hostapd_get_rsne_override_2_len(hapd);
+ tail_len += hostapd_get_rsnxe_override_len(hapd);
tailpos = tail = os_malloc(tail_len);
if (head == NULL || tail == NULL) {
@@ -2270,7 +2374,7 @@
tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
- tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON);
+ tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON, true);
tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
tailpos = hostapd_eid_mbssid_config(hapd, tailpos,
@@ -2349,6 +2453,13 @@
tail + tail_len - tailpos);
tailpos = hostapd_eid_dpp_cc(hapd, tailpos, tail + tail_len - tailpos);
+ tailpos = hostapd_get_rsne_override(hapd, tailpos,
+ tail + tail_len - tailpos);
+ tailpos = hostapd_get_rsne_override_2(hapd, tailpos,
+ tail + tail_len - tailpos);
+ tailpos = hostapd_get_rsnxe_override(hapd, tailpos,
+ tail + tail_len - tailpos);
+
if (hapd->conf->vendor_elements) {
os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
wpabuf_len(hapd->conf->vendor_elements));
@@ -2381,7 +2492,9 @@
/* If SAE offload is enabled, provide password to lower layer for
* SAE authentication and PMK generation.
*/
- if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+ if (wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt_2) &&
(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP)) {
if (hostapd_sae_pk_in_use(hapd->conf)) {
wpa_printf(MSG_ERROR,
@@ -2426,7 +2539,9 @@
else if (hapd->conf->wpa & WPA_PROTO_WPA)
params->pairwise_ciphers = hapd->conf->wpa_pairwise;
params->group_cipher = hapd->conf->wpa_group;
- params->key_mgmt_suites = hapd->conf->wpa_key_mgmt;
+ params->key_mgmt_suites = hapd->conf->wpa_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt_2;
params->auth_algs = hapd->conf->auth_algs;
params->wpa_version = hapd->conf->wpa;
params->privacy = hapd->conf->wpa;
@@ -2527,8 +2642,8 @@
params->fd_frame_tmpl = NULL;
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211AX
- os_free(params->unsol_bcast_probe_resp_tmpl);
- params->unsol_bcast_probe_resp_tmpl = NULL;
+ os_free(params->ubpr.unsol_bcast_probe_resp_tmpl);
+ params->ubpr.unsol_bcast_probe_resp_tmpl = NULL;
#endif /* CONFIG_IEEE80211AX */
os_free(params->allowed_freqs);
params->allowed_freqs = NULL;
@@ -2543,6 +2658,7 @@
struct hostapd_config *iconf = iface->conf;
struct hostapd_hw_modes *cmode = iface->current_mode;
struct wpabuf *beacon, *proberesp, *assocresp;
+ bool twt_he_responder = false;
int res, ret = -1, i;
struct hostapd_hw_modes *mode;
@@ -2586,11 +2702,13 @@
params.he_bss_color_partial =
hapd->iface->conf->he_op.he_bss_color_partial;
params.he_bss_color = hapd->iface->conf->he_op.he_bss_color;
- params.twt_responder = hostapd_get_he_twt_responder(hapd,
- IEEE80211_MODE_AP);
- params.unsol_bcast_probe_resp_tmpl =
- hostapd_unsol_bcast_probe_resp(hapd, ¶ms);
+ twt_he_responder = hostapd_get_he_twt_responder(hapd,
+ IEEE80211_MODE_AP);
+ params.ubpr.unsol_bcast_probe_resp_tmpl =
+ hostapd_unsol_bcast_probe_resp(hapd, ¶ms.ubpr);
#endif /* CONFIG_IEEE80211AX */
+ params.twt_responder =
+ twt_he_responder || hostapd_get_ht_vht_twt_responder(hapd);
hapd->reenable_beacon = 0;
#ifdef CONFIG_SAE
params.sae_pwe = hapd->conf->sae_pwe;
@@ -2617,8 +2735,14 @@
cmode->vht_capab,
&cmode->he_capab[IEEE80211_MODE_AP],
&cmode->eht_capab[IEEE80211_MODE_AP],
- hostapd_get_punct_bitmap(hapd)) == 0)
+ hostapd_get_punct_bitmap(hapd)) == 0) {
+ freq.link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ freq.link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
params.freq = &freq;
+ }
for (i = 0; i < hapd->iface->num_hw_features; i++) {
mode = &hapd->iface->hw_features[i];
@@ -2656,7 +2780,7 @@
struct hostapd_iface *iface = hapd->iface;
int ret;
size_t i, j;
- bool is_6g;
+ bool is_6g, hapd_mld = false;
ret = __ieee802_11_set_beacon(hapd);
if (ret != 0)
@@ -2665,26 +2789,33 @@
if (!iface->interfaces || iface->interfaces->count <= 1)
return 0;
+#ifdef CONFIG_IEEE80211BE
+ hapd_mld = hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
/* Update Beacon frames in case of 6 GHz colocation or AP MLD */
is_6g = is_6ghz_op_class(iface->conf->op_class);
for (j = 0; j < iface->interfaces->count; j++) {
struct hostapd_iface *other;
- bool mld_ap = false;
+ bool other_iface_6g;
other = iface->interfaces->iface[j];
if (other == iface || !other || !other->conf)
continue;
-#ifdef CONFIG_IEEE80211BE
- if (hostapd_is_ml_partner(hapd, other->bss[0]))
- mld_ap = true;
-#endif /* CONFIG_IEEE80211BE */
+ other_iface_6g = is_6ghz_op_class(other->conf->op_class);
- if (is_6g == is_6ghz_op_class(other->conf->op_class) &&
- !mld_ap)
+ if (is_6g == other_iface_6g && !hapd_mld)
continue;
for (i = 0; i < other->num_bss; i++) {
+#ifdef CONFIG_IEEE80211BE
+ if (is_6g == other_iface_6g &&
+ !(hapd_mld && other->bss[i]->conf->mld_ap &&
+ hostapd_is_ml_partner(hapd, other->bss[i])))
+ continue;
+#endif /* CONFIG_IEEE80211BE */
+
if (other->bss[i] && other->bss[i]->started)
__ieee802_11_set_beacon(other->bss[i]);
}
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index b32b2a7..e381542 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -33,4 +33,7 @@
const u8 * hostapd_wpa_ie(struct hostapd_data *hapd, u8 eid);
+u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
+ struct unsol_bcast_probe_resp *ubpr);
+
#endif /* BEACON_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index eac0606..d4d73de 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -277,6 +277,14 @@
return len;
len += ret;
+ if (sta->max_idle_period) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "max_idle_period=%d\n", sta->max_idle_period);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
if (res >= 0)
len += res;
@@ -902,6 +910,42 @@
return len;
len += ret;
}
+
+ if (hapd->conf->mld_ap) {
+ struct hostapd_data *link_bss;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "num_links=%d\n",
+ hapd->mld->num_links);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ /* Self BSS */
+ ret = os_snprintf(buf + len, buflen - len,
+ "link_id=%d\n"
+ "link_addr=" MACSTR "\n",
+ hapd->mld_link_id,
+ MAC2STR(hapd->own_addr));
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ /* Partner BSSs */
+ for_each_mld_link(link_bss, hapd) {
+ if (link_bss == hapd)
+ continue;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "partner_link[%d]=" MACSTR
+ "\n",
+ link_bss->mld_link_id,
+ MAC2STR(link_bss->own_addr));
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+ }
}
#endif /* CONFIG_IEEE80211BE */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index b0fcd1c..233984f 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -513,11 +513,15 @@
"Failed to initialize WPA state machine");
return -1;
}
+ wpa_auth_set_rsn_override(sta->wpa_sm,
+ elems.rsne_override != NULL);
+ wpa_auth_set_rsn_override_2(sta->wpa_sm,
+ elems.rsne_override_2 != NULL);
#ifdef CONFIG_IEEE80211BE
if (ap_sta_is_mld(hapd, sta)) {
wpa_printf(MSG_DEBUG,
"MLD: Set ML info in RSN Authenticator");
- wpa_auth_set_ml_info(sta->wpa_sm, hapd->mld->mld_addr,
+ wpa_auth_set_ml_info(sta->wpa_sm,
sta->mld_assoc_link_id,
&sta->mld_info);
}
@@ -528,7 +532,7 @@
elems.rsnxe ? elems.rsnxe - 2 : NULL,
elems.rsnxe ? elems.rsnxe_len + 2 : 0,
elems.mdie, elems.mdie_len,
- elems.owe_dh, elems.owe_dh_len);
+ elems.owe_dh, elems.owe_dh_len, NULL);
reason = WLAN_REASON_INVALID_IE;
status = WLAN_STATUS_INVALID_IE;
switch (res) {
@@ -1537,7 +1541,6 @@
#ifdef CONFIG_IEEE80211R_AP
static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst,
- const u8 *bssid,
u16 auth_transaction, u16 status,
const u8 *ies, size_t ies_len)
{
@@ -1613,7 +1616,7 @@
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
- wpa_ft_process_auth(sta->wpa_sm, rx_auth->bssid,
+ wpa_ft_process_auth(sta->wpa_sm,
rx_auth->auth_transaction, rx_auth->ies,
rx_auth->ies_len,
hostapd_notify_auth_ft_finish, hapd);
@@ -1780,7 +1783,7 @@
#define HAPD_BROADCAST ((struct hostapd_data *) -1)
static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
- const u8 *bssid)
+ const u8 *bssid, int link_id)
{
size_t i;
@@ -1791,8 +1794,35 @@
return HAPD_BROADCAST;
for (i = 0; i < iface->num_bss; i++) {
- if (ether_addr_equal(bssid, iface->bss[i]->own_addr))
- return iface->bss[i];
+ struct hostapd_data *hapd;
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *p_hapd;
+#endif /* CONFIG_IEEE80211BE */
+
+ hapd = iface->bss[i];
+ if (ether_addr_equal(bssid, hapd->own_addr))
+ return hapd;
+
+#ifdef CONFIG_IEEE80211BE
+ if (ether_addr_equal(bssid, hapd->own_addr) ||
+ (hapd->conf->mld_ap &&
+ ether_addr_equal(bssid, hapd->mld->mld_addr) &&
+ link_id == hapd->mld_link_id))
+ return hapd;
+
+ if (!hapd->conf->mld_ap)
+ continue;
+
+ for_each_mld_link(p_hapd, hapd) {
+ if (p_hapd == hapd)
+ continue;
+
+ if (ether_addr_equal(bssid, p_hapd->own_addr) ||
+ (ether_addr_equal(bssid, p_hapd->mld->mld_addr) &&
+ link_id == p_hapd->mld_link_id))
+ return p_hapd;
+ }
+#endif /* CONFIG_IEEE80211BE */
}
return NULL;
@@ -1803,7 +1833,7 @@
const u8 *bssid, const u8 *addr,
int wds)
{
- hapd = get_hapd_bssid(hapd->iface, bssid);
+ hapd = get_hapd_bssid(hapd->iface, bssid, -1);
if (hapd == NULL || hapd == HAPD_BROADCAST)
return;
@@ -1818,8 +1848,9 @@
const u8 *bssid;
struct hostapd_frame_info fi;
int ret;
- bool is_mld = false;
+ if (rx_mgmt->ctx)
+ hapd = rx_mgmt->ctx;
hapd = switch_link_hapd(hapd, rx_mgmt->link_id);
iface = hapd->iface;
@@ -1843,14 +1874,7 @@
if (bssid == NULL)
return 0;
-#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap &&
- ether_addr_equal(hapd->mld->mld_addr, bssid))
- is_mld = true;
-#endif /* CONFIG_IEEE80211BE */
-
- if (!is_mld)
- hapd = get_hapd_bssid(iface, bssid);
+ hapd = get_hapd_bssid(iface, bssid, rx_mgmt->link_id);
if (!hapd) {
u16 fc = le_to_host16(hdr->frame_control);
@@ -1902,17 +1926,11 @@
struct ieee80211_hdr *hdr;
struct hostapd_data *orig_hapd, *tmp_hapd;
-#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && link_id != -1) {
- tmp_hapd = hostapd_mld_get_link_bss(hapd, link_id);
- if (tmp_hapd)
- hapd = tmp_hapd;
- }
-#endif /* CONFIG_IEEE80211BE */
orig_hapd = hapd;
hdr = (struct ieee80211_hdr *) buf;
- tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+ hapd = switch_link_hapd(hapd, link_id);
+ tmp_hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len), link_id);
if (tmp_hapd) {
hapd = tmp_hapd;
#ifdef CONFIG_IEEE80211BE
@@ -1929,7 +1947,7 @@
if (stype != WLAN_FC_STYPE_ACTION || len <= 25 ||
buf[24] != WLAN_ACTION_PUBLIC)
return;
- hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2);
+ hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2, link_id);
if (!hapd || hapd == HAPD_BROADCAST)
return;
/*
@@ -1966,54 +1984,49 @@
static struct hostapd_data * hostapd_find_by_sta(struct hostapd_iface *iface,
- const u8 *src, bool rsn)
+ const u8 *src, bool rsn,
+ struct sta_info **sta_ret)
{
+ struct hostapd_data *hapd;
struct sta_info *sta;
unsigned int j;
+ if (sta_ret)
+ *sta_ret = NULL;
+
for (j = 0; j < iface->num_bss; j++) {
- sta = ap_get_sta(iface->bss[j], src);
+ hapd = iface->bss[j];
+ sta = ap_get_sta(hapd, src);
if (sta && (sta->flags & WLAN_STA_ASSOC) &&
- (!rsn || sta->wpa_sm))
- return iface->bss[j];
+ (!rsn || sta->wpa_sm)) {
+ if (sta_ret)
+ *sta_ret = sta;
+ return hapd;
+ }
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ struct hostapd_data *p_hapd;
+
+ for_each_mld_link(p_hapd, hapd) {
+ if (p_hapd == hapd)
+ continue;
+
+ sta = ap_get_sta(p_hapd, src);
+ if (sta && (sta->flags & WLAN_STA_ASSOC) &&
+ (!rsn || sta->wpa_sm)) {
+ if (sta_ret)
+ *sta_ret = sta;
+ return p_hapd;
+ }
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
}
return NULL;
}
-#ifdef CONFIG_IEEE80211BE
-static bool search_mld_sta(struct hostapd_data **p_hapd, const u8 *src)
-{
- struct hostapd_data *hapd = *p_hapd;
- unsigned int i;
-
- /* Search for STA on other MLO BSSs */
- for (i = 0; i < hapd->iface->interfaces->count; i++) {
- struct hostapd_iface *h =
- hapd->iface->interfaces->iface[i];
- struct hostapd_data *h_hapd = h->bss[0];
-
- if (!hostapd_is_ml_partner(h_hapd, hapd))
- continue;
-
- h_hapd = hostapd_find_by_sta(h, src, false);
- if (h_hapd) {
- struct sta_info *sta = ap_get_sta(h_hapd, src);
-
- if (sta && sta->mld_info.mld_sta &&
- sta->mld_assoc_link_id != h_hapd->mld_link_id)
- continue;
- *p_hapd = h_hapd;
- return true;
- }
- }
-
- return false;
-}
-#endif /* CONFIG_IEEE80211BE */
-
-
static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
const u8 *data, size_t data_len,
enum frame_encryption encrypted,
@@ -2022,28 +2035,10 @@
struct hostapd_data *orig_hapd = hapd;
#ifdef CONFIG_IEEE80211BE
- if (link_id != -1) {
- struct hostapd_data *h_hapd;
-
- hapd = switch_link_hapd(hapd, link_id);
- h_hapd = hostapd_find_by_sta(hapd->iface, src, true);
- if (!h_hapd)
- h_hapd = hostapd_find_by_sta(orig_hapd->iface, src,
- true);
- if (!h_hapd)
- h_hapd = hostapd_find_by_sta(hapd->iface, src, false);
- if (!h_hapd)
- h_hapd = hostapd_find_by_sta(orig_hapd->iface, src,
- false);
- if (h_hapd)
- hapd = h_hapd;
- } else if (hapd->conf->mld_ap) {
- search_mld_sta(&hapd, src);
- } else {
- hapd = hostapd_find_by_sta(hapd->iface, src, false);
- }
+ hapd = switch_link_hapd(hapd, link_id);
+ hapd = hostapd_find_by_sta(hapd->iface, src, true, NULL);
#else /* CONFIG_IEEE80211BE */
- hapd = hostapd_find_by_sta(hapd->iface, src, false);
+ hapd = hostapd_find_by_sta(hapd->iface, src, false, NULL);
#endif /* CONFIG_IEEE80211BE */
if (!hapd) {
@@ -2373,6 +2368,54 @@
#endif /* CONFIG_OWE */
+#ifdef NEED_AP_MLME
+static void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
+ const u8 *data, size_t len, int ack,
+ int link_id)
+{
+ struct sta_info *sta;
+
+ hapd = switch_link_hapd(hapd, link_id);
+ hapd = hostapd_find_by_sta(hapd->iface, dst, false, &sta);
+
+ if (!sta) {
+ wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
+ MACSTR " that is not currently associated",
+ MAC2STR(dst));
+ return;
+ }
+
+ ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
+}
+#endif /* NEED_AP_MLME */
+
+
+#ifdef CONFIG_IEEE80211AX
+static void hostapd_event_color_change(struct hostapd_data *hapd, bool success)
+{
+ struct hostapd_data *bss;
+ size_t i;
+
+ for (i = 0; i < hapd->iface->num_bss; i++) {
+ bss = hapd->iface->bss[i];
+ if (bss->cca_color == 0)
+ continue;
+
+ if (success)
+ hapd->iface->conf->he_op.he_bss_color = bss->cca_color;
+
+ bss->cca_in_progress = 0;
+ if (ieee802_11_set_beacon(bss)) {
+ wpa_printf(MSG_ERROR, "Failed to remove BCCA element");
+ bss->cca_in_progress = 1;
+ } else {
+ hostapd_cleanup_cca_params(bss);
+ }
+ }
+}
+#endif /* CONFIG_IEEE80211AX */
+
+
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
@@ -2451,11 +2494,11 @@
}
break;
case EVENT_EAPOL_TX_STATUS:
- hapd = switch_link_hapd(hapd, data->eapol_tx_status.link_id);
hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst,
data->eapol_tx_status.data,
data->eapol_tx_status.data_len,
- data->eapol_tx_status.ack);
+ data->eapol_tx_status.ack,
+ data->eapol_tx_status.link_id);
break;
case EVENT_DRIVER_CLIENT_POLL_OK:
hostapd_client_poll_ok(hapd, data->client_poll.addr);
@@ -2698,26 +2741,32 @@
/* The BSS color is shared amongst all BBSs on a specific phy.
* Therefore we always start the color change on the primary
* BSS. */
+ hapd = switch_link_hapd(hapd,
+ data->bss_color_collision.link_id);
wpa_printf(MSG_DEBUG, "BSS color collision on %s",
hapd->conf->iface);
hostapd_switch_color(hapd->iface->bss[0],
data->bss_color_collision.bitmap);
break;
case EVENT_CCA_STARTED_NOTIFY:
- wpa_printf(MSG_DEBUG, "CCA started on on %s",
+ hapd = switch_link_hapd(hapd,
+ data->bss_color_collision.link_id);
+ wpa_printf(MSG_DEBUG, "CCA started on %s",
hapd->conf->iface);
break;
case EVENT_CCA_ABORTED_NOTIFY:
- wpa_printf(MSG_DEBUG, "CCA aborted on on %s",
+ hapd = switch_link_hapd(hapd,
+ data->bss_color_collision.link_id);
+ wpa_printf(MSG_DEBUG, "CCA aborted on %s",
hapd->conf->iface);
- hostapd_cleanup_cca_params(hapd);
+ hostapd_event_color_change(hapd, false);
break;
case EVENT_CCA_NOTIFY:
- wpa_printf(MSG_DEBUG, "CCA finished on on %s",
+ hapd = switch_link_hapd(hapd,
+ data->bss_color_collision.link_id);
+ wpa_printf(MSG_DEBUG, "CCA finished on %s",
hapd->conf->iface);
- if (hapd->cca_color)
- hapd->iface->conf->he_op.he_bss_color = hapd->cca_color;
- hostapd_cleanup_cca_params(hapd);
+ hostapd_event_color_change(hapd, true);
break;
#endif /* CONFIG_IEEE80211AX */
default:
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 56bac45..a05de03 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1307,6 +1307,59 @@
}
+#ifndef CONFIG_NO_RADIUS
+static int hostapd_bss_radius_init(struct hostapd_data *hapd)
+{
+ struct hostapd_bss_config *conf;
+
+ if (!hapd)
+ return -1;
+
+ conf = hapd->conf;
+
+ if (hapd->radius) {
+ wpa_printf(MSG_DEBUG,
+ "Skipping RADIUS client init (already done)");
+ return 0;
+ }
+
+ hapd->radius = radius_client_init(hapd, conf->radius);
+ if (!hapd->radius) {
+ wpa_printf(MSG_ERROR,
+ "RADIUS client initialization failed.");
+ return -1;
+ }
+
+ if (conf->radius_das_port) {
+ struct radius_das_conf das_conf;
+
+ os_memset(&das_conf, 0, sizeof(das_conf));
+ das_conf.port = conf->radius_das_port;
+ das_conf.shared_secret = conf->radius_das_shared_secret;
+ das_conf.shared_secret_len =
+ conf->radius_das_shared_secret_len;
+ das_conf.client_addr = &conf->radius_das_client_addr;
+ das_conf.time_window = conf->radius_das_time_window;
+ das_conf.require_event_timestamp =
+ conf->radius_das_require_event_timestamp;
+ das_conf.require_message_authenticator =
+ conf->radius_das_require_message_authenticator;
+ das_conf.ctx = hapd;
+ das_conf.disconnect = hostapd_das_disconnect;
+ das_conf.coa = hostapd_das_coa;
+ hapd->radius_das = radius_das_init(&das_conf);
+ if (!hapd->radius_das) {
+ wpa_printf(MSG_ERROR,
+ "RADIUS DAS initialization failed.");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
/**
* hostapd_setup_bss - Per-BSS setup (initialization)
* @hapd: Pointer to BSS data
@@ -1379,6 +1432,23 @@
} while (mac_in_conf(hapd->iconf, hapd->own_addr));
}
+#ifdef CONFIG_IEEE80211BE
+ if (conf->mld_ap) {
+ struct hostapd_data *h_hapd;
+
+ h_hapd = hostapd_mld_get_first_bss(hapd);
+ if (h_hapd) {
+ hapd->drv_priv = h_hapd->drv_priv;
+ hapd->interface_added = h_hapd->interface_added;
+ hostapd_mld_add_link(hapd);
+ wpa_printf(MSG_DEBUG,
+ "Setup of non first link (%d) BSS of MLD %s",
+ hapd->mld_link_id, hapd->conf->iface);
+ goto setup_mld;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
hapd->interface_added = 1;
if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
conf->iface, addr, hapd,
@@ -1393,8 +1463,34 @@
if (!addr)
os_memcpy(hapd->own_addr, if_addr, ETH_ALEN);
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ wpa_printf(MSG_DEBUG,
+ "Setup of first link (%d) BSS of MLD %s",
+ hapd->mld_link_id, hapd->conf->iface);
+ os_memcpy(hapd->mld->mld_addr, hapd->own_addr,
+ ETH_ALEN);
+ hostapd_mld_add_link(hapd);
+ }
+#endif /* CONFIG_IEEE80211BE */
}
+#ifdef CONFIG_IEEE80211BE
+setup_mld:
+ if (hapd->conf->mld_ap && !first) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Set link_id=%u, mld_addr=" MACSTR
+ ", own_addr=" MACSTR,
+ hapd->mld_link_id, MAC2STR(hapd->mld->mld_addr),
+ MAC2STR(hapd->own_addr));
+
+ if (hostapd_drv_link_add(hapd, hapd->mld_link_id,
+ hapd->own_addr))
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
if (conf->wmm_enabled < 0)
conf->wmm_enabled = hapd->iconf->ieee80211n |
hapd->iconf->ieee80211ax;
@@ -1497,46 +1593,26 @@
#endif /* CONFIG_SQLITE */
if (hostapd_mld_is_first_bss(hapd)) {
- hapd->radius = radius_client_init(hapd, conf->radius);
- if (!hapd->radius) {
- wpa_printf(MSG_ERROR,
- "RADIUS client initialization failed.");
+ if (hostapd_bss_radius_init(hapd))
return -1;
- }
-
- if (conf->radius_das_port) {
- struct radius_das_conf das_conf;
-
- os_memset(&das_conf, 0, sizeof(das_conf));
- das_conf.port = conf->radius_das_port;
- das_conf.shared_secret = conf->radius_das_shared_secret;
- das_conf.shared_secret_len =
- conf->radius_das_shared_secret_len;
- das_conf.client_addr = &conf->radius_das_client_addr;
- das_conf.time_window = conf->radius_das_time_window;
- das_conf.require_event_timestamp =
- conf->radius_das_require_event_timestamp;
- das_conf.require_message_authenticator =
- conf->radius_das_require_message_authenticator;
- das_conf.ctx = hapd;
- das_conf.disconnect = hostapd_das_disconnect;
- das_conf.coa = hostapd_das_coa;
- hapd->radius_das = radius_das_init(&das_conf);
- if (!hapd->radius_das) {
- wpa_printf(MSG_ERROR,
- "RADIUS DAS initialization failed.");
- return -1;
- }
- }
} else {
#ifdef CONFIG_IEEE80211BE
struct hostapd_data *f_bss;
- wpa_printf(MSG_DEBUG,
- "MLD: Using RADIUS client of the first BSS");
f_bss = hostapd_mld_get_first_bss(hapd);
if (!f_bss)
return -1;
+
+ if (!f_bss->radius) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: First BSS RADIUS client does not exist. Init on its behalf");
+
+ if (hostapd_bss_radius_init(f_bss))
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Using RADIUS client of the first BSS");
hapd->radius = f_bss->radius;
hapd->radius_das = f_bss->radius_das;
#endif /* CONFIG_IEEE80211BE */
@@ -3299,10 +3375,10 @@
static void hostapd_cleanup_driver(const struct wpa_driver_ops *driver,
void *drv_priv, struct hostapd_iface *iface)
{
-#ifdef CONFIG_IEEE80211BE
if (!driver || !driver->hapd_deinit || !drv_priv)
return;
+#ifdef CONFIG_IEEE80211BE
/* In case of non-ML operation, de-init. But if ML operation exist,
* even if that's the last BSS in the interface, the driver (drv) could
* be in use for a different AP MLD. Hence, need to check if drv is
@@ -4045,7 +4121,7 @@
#ifdef NEED_AP_MLME
-static void free_beacon_data(struct beacon_data *beacon)
+void free_beacon_data(struct beacon_data *beacon)
{
os_free(beacon->head);
beacon->head = NULL;
@@ -4344,6 +4420,11 @@
settings->link_id = hapd->mld_link_id;
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_IEEE80211AX
+ settings->ubpr.unsol_bcast_probe_resp_tmpl =
+ hostapd_unsol_bcast_probe_resp(hapd, &settings->ubpr);
+#endif /* CONFIG_IEEE80211AX */
+
return 0;
}
@@ -4405,6 +4486,9 @@
ret = hostapd_drv_switch_channel(hapd, settings);
free_beacon_data(&settings->beacon_csa);
free_beacon_data(&settings->beacon_after);
+#ifdef CONFIG_IEEE80211AX
+ os_free(settings->ubpr.unsol_bcast_probe_resp_tmpl);
+#endif /* CONFIG_IEEE80211AX */
if (ret) {
/* if we failed, clean cs parameters */
@@ -4421,15 +4505,16 @@
hostapd_switch_channel_fallback(struct hostapd_iface *iface,
const struct hostapd_freq_params *freq_params)
{
- int seg0_idx = 0, seg1_idx = 0;
+ u8 seg0_idx = 0, seg1_idx = 0;
enum oper_chan_width bw = CONF_OPER_CHWIDTH_USE_HT;
+ u8 op_class, chan = 0;
wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
if (freq_params->center_freq1)
- seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5;
+ ieee80211_freq_to_chan(freq_params->center_freq1, &seg0_idx);
if (freq_params->center_freq2)
- seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5;
+ ieee80211_freq_to_chan(freq_params->center_freq2, &seg1_idx);
switch (freq_params->bandwidth) {
case 0:
@@ -4462,6 +4547,15 @@
iface->freq = freq_params->freq;
iface->conf->channel = freq_params->channel;
iface->conf->secondary_channel = freq_params->sec_channel_offset;
+ if (ieee80211_freq_to_channel_ext(freq_params->freq,
+ freq_params->sec_channel_offset, bw,
+ &op_class, &chan) ==
+ NUM_HOSTAPD_MODES ||
+ chan != freq_params->channel)
+ wpa_printf(MSG_INFO, "CSA: Channel mismatch: %d -> %d",
+ freq_params->channel, chan);
+
+ iface->conf->op_class = op_class;
hostapd_set_oper_centr_freq_seg0_idx(iface->conf, seg0_idx);
hostapd_set_oper_centr_freq_seg1_idx(iface->conf, seg1_idx);
hostapd_set_oper_chwidth(iface->conf, bw);
@@ -4492,8 +4586,8 @@
}
-static int hostapd_fill_cca_settings(struct hostapd_data *hapd,
- struct cca_settings *settings)
+int hostapd_fill_cca_settings(struct hostapd_data *hapd,
+ struct cca_settings *settings)
{
struct hostapd_iface *iface = hapd->iface;
u8 old_color;
@@ -4502,6 +4596,12 @@
if (!iface || iface->conf->he_op.he_bss_color_disabled)
return -1;
+ settings->link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ settings->link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
+
old_color = iface->conf->he_op.he_bss_color;
iface->conf->he_op.he_bss_color = hapd->cca_color;
ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
@@ -4520,6 +4620,9 @@
return ret;
}
+ settings->ubpr.unsol_bcast_probe_resp_tmpl =
+ hostapd_unsol_bcast_probe_resp(hapd, &settings->ubpr);
+
settings->counter_offset_beacon = hapd->cca_c_off_beacon;
settings->counter_offset_presp = hapd->cca_c_off_proberesp;
@@ -4582,6 +4685,7 @@
free_beacon_data(&settings.beacon_cca);
free_beacon_data(&settings.beacon_after);
+ os_free(settings.ubpr.unsol_bcast_probe_resp_tmpl);
}
}
@@ -4681,17 +4785,28 @@
struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
u8 link_id)
{
- unsigned int i;
+ struct hostapd_iface *iface;
+ struct hostapd_data *bss;
+ unsigned int i, j;
for (i = 0; i < hapd->iface->interfaces->count; i++) {
- struct hostapd_iface *h = hapd->iface->interfaces->iface[i];
- struct hostapd_data *h_hapd = h->bss[0];
-
- if (!hostapd_is_ml_partner(hapd, h_hapd))
+ iface = hapd->iface->interfaces->iface[i];
+ if (!iface)
continue;
- if (h_hapd->mld_link_id == link_id)
- return h_hapd;
+ for (j = 0; j < iface->num_bss; j++) {
+ bss = iface->bss[j];
+
+ if (!bss->conf->mld_ap ||
+ !hostapd_is_ml_partner(hapd, bss))
+ continue;
+
+ if (!bss->drv_priv)
+ continue;
+
+ if (bss->mld_link_id == link_id)
+ return bss;
+ }
}
return NULL;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index ff29726..85122d4 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -684,6 +684,7 @@
#ifdef CONFIG_ACS
unsigned int acs_num_completed_scans;
+ unsigned int acs_num_retries;
#endif /* CONFIG_ACS */
void (*scan_cb)(struct hostapd_iface *iface);
@@ -825,23 +826,16 @@
int hostapd_mld_remove_link(struct hostapd_data *hapd);
struct hostapd_data * hostapd_mld_get_first_bss(struct hostapd_data *hapd);
+void free_beacon_data(struct beacon_data *beacon);
+int hostapd_fill_cca_settings(struct hostapd_data *hapd,
+ struct cca_settings *settings);
+
#ifdef CONFIG_IEEE80211BE
bool hostapd_mld_is_first_bss(struct hostapd_data *hapd);
-#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
- for (_iface_idx = 0; \
- _iface_idx < (_ifaces)->count; \
- _iface_idx++) \
- for (_bss_idx = 0; \
- _bss_idx < \
- (_ifaces)->iface[_iface_idx]->num_bss; \
- _bss_idx++) \
- for (_link = \
- (_ifaces)->iface[_iface_idx]->bss[_bss_idx]; \
- _link && _link->conf->mld_ap && \
- hostapd_get_mld_id(_link) == _mld_id; \
- _link = NULL)
+#define for_each_mld_link(partner, self) \
+ dl_list_for_each(partner, &self->mld->links, struct hostapd_data, link)
#else /* CONFIG_IEEE80211BE */
@@ -850,7 +844,7 @@
return true;
}
-#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
+#define for_each_mld_link(partner, self) \
if (false)
#endif /* CONFIG_IEEE80211BE */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 85a39d5..1cd76ca 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -404,7 +404,7 @@
static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
- const u8 *dst, const u8 *bssid,
+ const u8 *dst,
u16 auth_alg, u16 auth_transaction, u16 resp,
const u8 *ies, size_t ies_len, const char *dbg)
{
@@ -416,14 +416,7 @@
struct wpabuf *ml_resp = NULL;
#ifdef CONFIG_IEEE80211BE
- /*
- * Once a non-AP MLD is added to the driver, the addressing should use
- * the MLD MAC address. Thus, use the MLD address instead of translating
- * the addresses.
- */
if (ap_sta_is_mld(hapd, sta)) {
- sa = hapd->mld->mld_addr;
-
ml_resp = hostapd_ml_auth_resp(hapd);
if (!ml_resp)
return -1;
@@ -444,7 +437,7 @@
WLAN_FC_STYPE_AUTH);
os_memcpy(reply->da, dst, ETH_ALEN);
os_memcpy(reply->sa, sa, ETH_ALEN);
- os_memcpy(reply->bssid, bssid, ETH_ALEN);
+ os_memcpy(reply->bssid, sa, ETH_ALEN);
reply->u.auth.auth_alg = host_to_le16(auth_alg);
reply->u.auth.auth_transaction = host_to_le16(auth_transaction);
@@ -508,7 +501,7 @@
#ifdef CONFIG_IEEE80211R_AP
-static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
+static void handle_auth_ft_finish(void *ctx, const u8 *dst,
u16 auth_transaction, u16 status,
const u8 *ies, size_t ies_len)
{
@@ -516,7 +509,7 @@
struct sta_info *sta;
int reply_res;
- reply_res = send_auth_reply(hapd, NULL, dst, bssid, WLAN_AUTH_FT,
+ reply_res = send_auth_reply(hapd, NULL, dst, WLAN_AUTH_FT,
auth_transaction, status, ies, ies_len,
"auth-ft-finish");
@@ -712,7 +705,7 @@
static int auth_sae_send_commit(struct hostapd_data *hapd,
struct sta_info *sta,
- const u8 *bssid, int update, int status_code)
+ int update, int status_code)
{
struct wpabuf *data;
int reply_res;
@@ -739,7 +732,7 @@
status = hapd->conf->sae_commit_status;
}
#endif /* CONFIG_TESTING_OPTIONS */
- reply_res = send_auth_reply(hapd, sta, sta->addr, bssid,
+ reply_res = send_auth_reply(hapd, sta, sta->addr,
WLAN_AUTH_SAE, 1,
status, wpabuf_head(data),
wpabuf_len(data), "sae-send-commit");
@@ -751,8 +744,7 @@
static int auth_sae_send_confirm(struct hostapd_data *hapd,
- struct sta_info *sta,
- const u8 *bssid)
+ struct sta_info *sta)
{
struct wpabuf *data;
int reply_res;
@@ -761,7 +753,7 @@
if (data == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- reply_res = send_auth_reply(hapd, sta, sta->addr, bssid,
+ reply_res = send_auth_reply(hapd, sta, sta->addr,
WLAN_AUTH_SAE, 2,
WLAN_STATUS_SUCCESS, wpabuf_head(data),
wpabuf_len(data), "sae-send-confirm");
@@ -821,12 +813,41 @@
if (sta->sae->sync > hapd->conf->sae_sync) {
sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync");
sta->sae->sync = 0;
+ if (sta->sae->tmp) {
+ /* Disable this SAE instance for 10 seconds to avoid
+ * unnecessary flood of multiple SAE commits in
+ * unexpected mesh cases. */
+ if (os_get_reltime(&sta->sae->tmp->disabled_until) == 0)
+ sta->sae->tmp->disabled_until.sec += 10;
+ }
return -1;
}
return 0;
}
+static bool sae_proto_instance_disabled(struct sta_info *sta)
+{
+ struct sae_temporary_data *tmp;
+
+ if (!sta->sae)
+ return false;
+ tmp = sta->sae->tmp;
+ if (!tmp)
+ return false;
+
+ if (os_reltime_initialized(&tmp->disabled_until)) {
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+ if (os_reltime_before(&now, &tmp->disabled_until))
+ return true;
+ }
+
+ return false;
+}
+
+
static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data)
{
struct hostapd_data *hapd = eloop_ctx;
@@ -843,13 +864,13 @@
switch (sta->sae->state) {
case SAE_COMMITTED:
- ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1);
+ ret = auth_sae_send_commit(hapd, sta, 0, -1);
eloop_register_timeout(0,
hapd->dot11RSNASAERetransPeriod * 1000,
auth_sae_retransmit_timer, hapd, sta);
break;
case SAE_CONFIRMED:
- ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr);
+ ret = auth_sae_send_confirm(hapd, sta);
eloop_register_timeout(0,
hapd->dot11RSNASAERetransPeriod * 1000,
auth_sae_retransmit_timer, hapd, sta);
@@ -957,7 +978,7 @@
static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
- const u8 *bssid, u16 auth_transaction, u16 status_code,
+ u16 auth_transaction, u16 status_code,
int allow_reuse, int *sta_removed)
{
int ret;
@@ -970,6 +991,13 @@
wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u",
MAC2STR(sta->addr), sae_state_txt(sta->sae->state),
auth_transaction);
+
+ if (auth_transaction == 1 && sae_proto_instance_disabled(sta)) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Protocol instance temporarily disabled - discard received SAE commit");
+ return WLAN_STATUS_SUCCESS;
+ }
+
switch (sta->sae->state) {
case SAE_NOTHING:
if (auth_transaction == 1) {
@@ -981,7 +1009,7 @@
sta->sae->pk =
status_code == WLAN_STATUS_SAE_PK;
}
- ret = auth_sae_send_commit(hapd, sta, bssid,
+ ret = auth_sae_send_commit(hapd, sta,
!allow_reuse, status_code);
if (ret)
return ret;
@@ -1007,7 +1035,7 @@
* based on SAE finite state machine
* Nothing -> Confirm transition.
*/
- ret = auth_sae_send_confirm(hapd, sta, bssid);
+ ret = auth_sae_send_confirm(hapd, sta);
if (ret)
return ret;
sae_set_state(sta, SAE_CONFIRMED,
@@ -1037,7 +1065,7 @@
if (sae_process_commit(sta->sae) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- ret = auth_sae_send_confirm(hapd, sta, bssid);
+ ret = auth_sae_send_confirm(hapd, sta);
if (ret)
return ret;
sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
@@ -1052,8 +1080,7 @@
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
- ret = auth_sae_send_commit(hapd, sta, bssid, 0,
- status_code);
+ ret = auth_sae_send_commit(hapd, sta, 0, status_code);
if (ret)
return ret;
@@ -1064,7 +1091,7 @@
* Nothing -> Confirmed transition that was reduced to
* Nothing -> Committed above.
*/
- ret = auth_sae_send_confirm(hapd, sta, bssid);
+ ret = auth_sae_send_confirm(hapd, sta);
if (ret)
return ret;
@@ -1075,7 +1102,7 @@
* step to get to Accepted without waiting for
* additional events.
*/
- return sae_sm_step(hapd, sta, bssid, auth_transaction,
+ return sae_sm_step(hapd, sta, auth_transaction,
WLAN_STATUS_SUCCESS, 0, sta_removed);
}
break;
@@ -1086,15 +1113,14 @@
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
- ret = auth_sae_send_commit(hapd, sta, bssid, 1,
- status_code);
+ ret = auth_sae_send_commit(hapd, sta, 1, status_code);
if (ret)
return ret;
if (sae_process_commit(sta->sae) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- ret = auth_sae_send_confirm(hapd, sta, bssid);
+ ret = auth_sae_send_confirm(hapd, sta);
if (ret)
return ret;
@@ -1115,8 +1141,7 @@
*sta_removed = 1;
} else if (auth_transaction == 1) {
wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
- ret = auth_sae_send_commit(hapd, sta, bssid, 1,
- status_code);
+ ret = auth_sae_send_commit(hapd, sta, 1, status_code);
if (ret)
return ret;
sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
@@ -1130,7 +1155,7 @@
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
- ret = auth_sae_send_confirm(hapd, sta, bssid);
+ ret = auth_sae_send_confirm(hapd, sta);
sae_clear_temp_data(sta->sae);
if (ret)
return ret;
@@ -1242,7 +1267,7 @@
struct sae_data *sae)
{
const struct wpabuf *groups;
- size_t i, count;
+ size_t i, count, len;
const u8 *pos;
if (!sae->tmp)
@@ -1252,7 +1277,15 @@
return 0;
pos = wpabuf_head(groups);
- count = wpabuf_len(groups) / 2;
+ len = wpabuf_len(groups);
+ if (len & 1) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Invalid length of the Rejected Groups element payload: %zu",
+ len);
+ return 1;
+ }
+
+ count = len / 2;
for (i = 0; i < count; i++) {
int enabled;
u16 group;
@@ -1291,7 +1324,7 @@
pos = mgmt->u.auth.variable;
end = ((const u8 *) mgmt) + len;
resp = status_code;
- send_auth_reply(hapd, sta, sta->addr, mgmt->bssid,
+ send_auth_reply(hapd, sta, sta->addr,
WLAN_AUTH_SAE,
auth_transaction, resp, pos, end - pos,
"auth-sae-reflection-attack");
@@ -1300,7 +1333,7 @@
if (hapd->conf->sae_commit_override && auth_transaction == 1) {
wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
- send_auth_reply(hapd, sta, sta->addr, mgmt->bssid,
+ send_auth_reply(hapd, sta, sta->addr,
WLAN_AUTH_SAE,
auth_transaction, resp,
wpabuf_head(hapd->conf->sae_commit_override),
@@ -1380,8 +1413,7 @@
* Authentication frame, and the commit-scalar and
* COMMIT-ELEMENT previously sent.
*/
- resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0,
- status_code);
+ resp = auth_sae_send_commit(hapd, sta, 0, status_code);
if (resp != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_ERROR,
"SAE: Failed to send commit message");
@@ -1407,6 +1439,12 @@
if (!sae_status_success(hapd, status_code))
goto remove_sta;
+ if (sae_proto_instance_disabled(sta)) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Protocol instance temporarily disabled - discard received SAE commit");
+ return;
+ }
+
if (!(hapd->conf->mesh & MESH_ENABLED) &&
sta->sae->state == SAE_COMMITTED) {
/* This is needed in the infrastructure BSS case to
@@ -1504,7 +1542,7 @@
goto reply;
}
- resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction,
+ resp = sae_sm_step(hapd, sta, auth_transaction,
status_code, allow_reuse, &sta_removed);
} else if (auth_transaction == 2) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -1547,7 +1585,7 @@
}
sta->sae->rc = peer_send_confirm;
}
- resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction,
+ resp = sae_sm_step(hapd, sta, auth_transaction,
status_code, 0, &sta_removed);
} else {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -1572,11 +1610,19 @@
data = wpabuf_alloc_copy(pos, 2);
sae_sme_send_external_auth_status(hapd, sta, resp);
- send_auth_reply(hapd, sta, sta->addr, mgmt->bssid,
+ send_auth_reply(hapd, sta, sta->addr,
WLAN_AUTH_SAE,
auth_transaction, resp,
data ? wpabuf_head(data) : (u8 *) "",
data ? wpabuf_len(data) : 0, "auth-sae");
+ if (sta->sae && sta->sae->tmp && sta->sae->tmp->pw_id &&
+ resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER &&
+ auth_transaction == 1) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Clear stored password identifier since this SAE commit was not accepted");
+ os_free(sta->sae->tmp->pw_id);
+ sta->sae->tmp->pw_id = NULL;
+ }
}
remove_sta:
@@ -1613,7 +1659,7 @@
if (sta->sae->state != SAE_NOTHING)
return -1;
- ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1);
+ ret = auth_sae_send_commit(hapd, sta, 0, -1);
if (ret)
return -1;
@@ -1894,11 +1940,14 @@
elems.rsn_ie - 2, elems.rsn_ie_len + 2,
elems.rsnxe ? elems.rsnxe - 2 : NULL,
elems.rsnxe ? elems.rsnxe_len + 2 : 0,
- elems.mdie, elems.mdie_len, NULL, 0);
+ elems.mdie, elems.mdie_len, NULL, 0, NULL);
resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
goto fail;
+ wpa_auth_set_rsn_override(sta->wpa_sm, elems.rsne_override != NULL);
+ wpa_auth_set_rsn_override_2(sta->wpa_sm, elems.rsne_override_2 != NULL);
+
if (!elems.fils_nonce) {
wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -2226,7 +2275,7 @@
auth_alg = (pub ||
resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ?
WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
- send_auth_reply(hapd, sta, sta->addr, hapd->own_addr, auth_alg, 2, resp,
+ send_auth_reply(hapd, sta, sta->addr, auth_alg, 2, resp,
data ? wpabuf_head(data) : (u8 *) "",
data ? wpabuf_len(data) : 0, "auth-fils-finish");
wpabuf_free(data);
@@ -2697,6 +2746,14 @@
pasn_set_akmp(pasn, rsn_data.key_mgmt);
pasn_set_cipher(pasn, rsn_data.pairwise_cipher);
+ if (pasn->derive_kdk &&
+ !ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
+ WLAN_RSNX_CAPAB_SECURE_LTF))
+ pasn_disable_kdk_derivation(pasn);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->force_kdk_derivation)
+ pasn_enable_kdk_derivation(pasn);
+#endif /* CONFIG_TESTING_OPTIONS */
akmp = pasn_get_akmp(pasn);
if (wpa_key_mgmt_ft(akmp) && rsn_data.num_pmkid) {
@@ -2827,7 +2884,7 @@
size_t resp_ies_len = 0;
u16 seq_ctrl;
struct radius_sta rad_info;
- const u8 *dst, *sa, *bssid;
+ const u8 *dst, *sa;
#ifdef CONFIG_IEEE80211BE
bool mld_sta = false;
#endif /* CONFIG_IEEE80211BE */
@@ -2907,7 +2964,10 @@
auth_alg == WLAN_AUTH_FT) ||
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
- (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+ (hapd->conf->wpa &&
+ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt |
+ hapd->conf->rsn_override_key_mgmt_2) &&
auth_alg == WLAN_AUTH_SAE) ||
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
@@ -3214,7 +3274,7 @@
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
- wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid,
+ wpa_ft_process_auth(sta->wpa_sm,
auth_transaction, mgmt->u.auth.variable,
len - IEEE80211_HDRLEN -
sizeof(mgmt->u.auth),
@@ -3262,21 +3322,13 @@
fail:
dst = mgmt->sa;
- bssid = mgmt->bssid;
#ifdef CONFIG_IEEE80211BE
- /*
- * Once a non-AP MLD is added to the driver, the addressing should use
- * the MLD MAC address. It is the responsibility of the driver to
- * handle the translations.
- */
- if (ap_sta_is_mld(hapd, sta)) {
+ if (ap_sta_is_mld(hapd, sta))
dst = sta->addr;
- bssid = hapd->mld->mld_addr;
- }
#endif /* CONFIG_IEEE80211BE */
- reply_res = send_auth_reply(hapd, sta, dst, bssid, auth_alg,
+ reply_res = send_auth_reply(hapd, sta, dst, auth_alg,
auth_alg == WLAN_AUTH_SAE ?
auth_transaction : auth_transaction + 1,
resp, resp_ies, resp_ies_len,
@@ -3783,14 +3835,14 @@
}
#ifdef CONFIG_IEEE80211BE
if (ap_sta_is_mld(hapd, sta))
- wpa_auth_set_ml_info(sta->wpa_sm, hapd->mld->mld_addr,
+ wpa_auth_set_ml_info(sta->wpa_sm,
sta->mld_assoc_link_id, &sta->mld_info);
#endif /* CONFIG_IEEE80211BE */
rsn_ie -= 2;
rsn_ie_len += 2;
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
hapd->iface->freq, rsn_ie, rsn_ie_len,
- NULL, 0, NULL, 0, owe_dh, owe_dh_len);
+ NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL);
status = wpa_res_to_status_code(res);
if (status != WLAN_STATUS_SUCCESS)
goto end;
@@ -3879,6 +3931,8 @@
const u8 *wpa_ie;
size_t wpa_ie_len;
const u8 *p2p_dev_addr = NULL;
+ struct hostapd_data *assoc_hapd;
+ struct sta_info *assoc_sta = NULL;
resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len);
if (resp != WLAN_STATUS_SUCCESS)
@@ -4044,14 +4098,18 @@
if (hapd->conf->wpa && wpa_ie) {
enum wpa_validate_result res;
+#ifdef CONFIG_IEEE80211BE
+ struct mld_info *info = &sta->mld_info;
+ bool init = !sta->wpa_sm;
+#endif /* CONFIG_IEEE80211BE */
wpa_ie -= 2;
wpa_ie_len += 2;
if (!sta->wpa_sm) {
-#ifdef CONFIG_IEEE80211BE
- struct mld_info *info = &sta->mld_info;
-#endif /* CONFIG_IEEE80211BE */
+ if (!link)
+ assoc_sta = hostapd_ml_get_assoc_sta(
+ hapd, sta, &assoc_hapd);
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
sta->addr,
@@ -4062,20 +4120,24 @@
"Failed to initialize RSN state machine");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
-
-#ifdef CONFIG_IEEE80211BE
- if (ap_sta_is_mld(hapd, sta)) {
- wpa_printf(MSG_DEBUG,
- "MLD: Set ML info in RSN Authenticator");
- wpa_auth_set_ml_info(sta->wpa_sm,
- hapd->mld->mld_addr,
- sta->mld_assoc_link_id,
- info);
- }
-#endif /* CONFIG_IEEE80211BE */
}
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: %s ML info in RSN Authenticator",
+ init ? "Set" : "Reset");
+ wpa_auth_set_ml_info(sta->wpa_sm,
+ sta->mld_assoc_link_id,
+ info);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
+ wpa_auth_set_rsn_override(sta->wpa_sm,
+ elems->rsne_override != NULL);
+ wpa_auth_set_rsn_override_2(sta->wpa_sm,
+ elems->rsne_override_2 != NULL);
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
hapd->iface->freq,
wpa_ie, wpa_ie_len,
@@ -4084,7 +4146,8 @@
elems->rsnxe ? elems->rsnxe_len + 2 :
0,
elems->mdie, elems->mdie_len,
- elems->owe_dh, elems->owe_dh_len);
+ elems->owe_dh, elems->owe_dh_len,
+ assoc_sta ? assoc_sta->wpa_sm : NULL);
resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
@@ -4205,6 +4268,13 @@
"association");
return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
}
+
+ wpa_auth_set_ssid_protection(
+ sta->wpa_sm,
+ hapd->conf->ssid_protection &&
+ ieee802_11_rsnx_capab_len(
+ elems->rsnxe, elems->rsnxe_len,
+ WLAN_RSNX_CAPAB_SSID_PROTECTION));
#ifdef CONFIG_HS20
} else if (hapd->conf->osen) {
if (!elems->osen) {
@@ -4341,6 +4411,19 @@
sta->power_capab = 0;
}
+ if (elems->bss_max_idle_period &&
+ hapd->conf->max_acceptable_idle_period) {
+ u16 req;
+
+ req = WPA_GET_LE16(elems->bss_max_idle_period);
+ if (req <= hapd->conf->max_acceptable_idle_period)
+ sta->max_idle_period = req;
+ else if (hapd->conf->max_acceptable_idle_period >
+ hapd->conf->ap_max_inactivity)
+ sta->max_idle_period =
+ hapd->conf->max_acceptable_idle_period;
+ }
+
return WLAN_STATUS_SUCCESS;
}
@@ -4475,6 +4558,8 @@
}
sta->flags |= origin_sta->flags | WLAN_STA_ASSOC_REQ_OK;
+ sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
+
status = __check_assoc_ies(hapd, sta, NULL, 0, &elems, reassoc, true);
if (status != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG, "MLD: link: Element check failed");
@@ -4482,7 +4567,6 @@
}
ap_sta_set_mld(sta, true);
- sta->mld_assoc_link_id = origin_sta->mld_assoc_link_id;
os_memcpy(&sta->mld_info, &origin_sta->mld_info, sizeof(sta->mld_info));
for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
@@ -4509,9 +4593,11 @@
ieee802_11_update_beacons(hapd->iface);
}
- /* RSN Authenticator should always be the one on the original station */
+ /* Maintain state machine reference on all link STAs, this is needed
+ * during group rekey handling.
+ */
wpa_auth_sta_deinit(sta->wpa_sm);
- sta->wpa_sm = NULL;
+ sta->wpa_sm = origin_sta->wpa_sm;
/*
* Do not initialize the EAPOL state machine.
@@ -4522,13 +4608,6 @@
wpa_printf(MSG_DEBUG, "MLD: link=%u, association OK (aid=%u)",
hapd->mld_link_id, sta->aid);
- /*
- * Get RSNE and RSNXE for the current BSS as they are required by the
- * Authenticator.
- */
- link->rsne = hostapd_wpa_ie(hapd, WLAN_EID_RSN);
- link->rsnxe = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
-
sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC_REQ_OK;
/* TODO: What other processing is required? */
@@ -4575,40 +4654,31 @@
bool offload)
{
#ifdef CONFIG_IEEE80211BE
- unsigned int i, j;
+ unsigned int i;
if (!hostapd_is_mld_ap(hapd))
return 0;
- /*
- * This is not really needed, but make the interaction with the RSN
- * Authenticator more consistent
- */
- sta->mld_info.links[hapd->mld_link_id].rsne =
- hostapd_wpa_ie(hapd, WLAN_EID_RSN);
- sta->mld_info.links[hapd->mld_link_id].rsnxe =
- hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
-
for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
- struct hostapd_iface *iface = NULL;
+ struct hostapd_data *bss = NULL;
struct mld_link_info *link = &sta->mld_info.links[i];
+ bool link_bss_found = false;
- if (!link->valid)
+ if (!link->valid || i == sta->mld_assoc_link_id)
continue;
- for (j = 0; j < hapd->iface->interfaces->count; j++) {
- iface = hapd->iface->interfaces->iface[j];
-
- if (hapd->iface == iface)
+ for_each_mld_link(bss, hapd) {
+ if (bss == hapd)
continue;
- if (hostapd_is_ml_partner(hapd, iface->bss[0]) &&
- i == iface->bss[0]->mld_link_id)
- break;
+ if (bss->mld_link_id != i)
+ continue;
+
+ link_bss_found = true;
+ break;
}
- if (!iface || j == hapd->iface->interfaces->count ||
- TEST_FAIL()) {
+ if (!link_bss_found || TEST_FAIL()) {
wpa_printf(MSG_DEBUG,
"MLD: No link match for link_id=%u", i);
@@ -4621,7 +4691,7 @@
if (!offload)
ieee80211_ml_build_assoc_resp(hapd, link);
} else {
- if (ieee80211_ml_process_link(iface->bss[0], sta, link,
+ if (ieee80211_ml_process_link(bss, sta, link,
ies, ies_len, reassoc,
offload))
return -1;
@@ -4786,7 +4856,6 @@
struct ieee80211_mgmt *reply;
u8 *p;
u16 res = WLAN_STATUS_SUCCESS;
- const u8 *sa = hapd->own_addr;
buflen = sizeof(struct ieee80211_mgmt) + 1024;
#ifdef CONFIG_FILS
@@ -4823,18 +4892,9 @@
(reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
WLAN_FC_STYPE_ASSOC_RESP));
-#ifdef CONFIG_IEEE80211BE
- /*
- * Once a non-AP MLD is added to the driver, the addressing should use
- * MLD MAC address.
- */
- if (ap_sta_is_mld(hapd, sta) && allow_mld_addr_trans)
- sa = hapd->mld->mld_addr;
-#endif /* CONFIG_IEEE80211BE */
-
os_memcpy(reply->da, addr, ETH_ALEN);
- os_memcpy(reply->sa, sa, ETH_ALEN);
- os_memcpy(reply->bssid, sa, ETH_ALEN);
+ os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
send_len = IEEE80211_HDRLEN;
send_len += sizeof(reply->u.assoc_resp);
@@ -4938,7 +4998,7 @@
#endif /* CONFIG_IEEE80211AX */
p = hostapd_eid_ext_capab(hapd, p, false);
- p = hostapd_eid_bss_max_idle_period(hapd, p);
+ p = hostapd_eid_bss_max_idle_period(hapd, p, sta->max_idle_period);
if (sta && sta->qos_map_enabled)
p = hostapd_eid_qos_map_set(hapd, p);
@@ -5622,7 +5682,7 @@
#endif /* CONFIG_FILS */
if (set_beacon)
- ieee802_11_set_beacons(hapd->iface);
+ ieee802_11_update_beacons(hapd->iface);
fail:
@@ -5794,7 +5854,7 @@
#ifdef CONFIG_IEEE80211BE
struct hostapd_data *assoc_hapd, *tmp_hapd;
struct sta_info *assoc_sta;
- unsigned int i, link_id;
+ struct sta_info *tmp_sta;
if (!hostapd_is_mld_ap(hapd))
return false;
@@ -5807,45 +5867,25 @@
if (!assoc_sta)
return false;
- for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- for (i = 0; i < assoc_hapd->iface->interfaces->count; i++) {
- struct sta_info *tmp_sta;
+ for_each_mld_link(tmp_hapd, assoc_hapd) {
+ if (tmp_hapd == assoc_hapd)
+ continue;
- if (!assoc_sta->mld_info.links[link_id].valid)
+ if (!assoc_sta->mld_info.links[tmp_hapd->mld_link_id].valid)
+ continue;
+
+ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+ tmp_sta = tmp_sta->next) {
+ if (tmp_sta->mld_assoc_link_id !=
+ assoc_sta->mld_assoc_link_id ||
+ tmp_sta->aid != assoc_sta->aid)
continue;
- tmp_hapd =
- assoc_hapd->iface->interfaces->iface[i]->bss[0];
-
- if (!hostapd_is_ml_partner(assoc_hapd, tmp_hapd))
- continue;
-
- for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
- tmp_sta = tmp_sta->next) {
- /*
- * Remove the station on which the association
- * was done only after all other link stations
- * are removed. Since there is only a single
- * station per struct hostapd_hapd with the
- * same association link simply break out from
- * the loop.
- */
- if (tmp_sta == assoc_sta)
- break;
-
- if (tmp_sta->mld_assoc_link_id !=
- assoc_sta->mld_assoc_link_id ||
- tmp_sta->aid != assoc_sta->aid)
- continue;
-
- if (!disassoc)
- hostapd_deauth_sta(tmp_hapd, tmp_sta,
- mgmt);
- else
- hostapd_disassoc_sta(tmp_hapd, tmp_sta,
- mgmt);
- break;
- }
+ if (!disassoc)
+ hostapd_deauth_sta(tmp_hapd, tmp_sta, mgmt);
+ else
+ hostapd_disassoc_sta(tmp_hapd, tmp_sta, mgmt);
+ break;
}
}
@@ -6468,38 +6508,33 @@
struct sta_info *sta, bool ok)
{
#ifdef CONFIG_IEEE80211BE
- unsigned int i, link_id;
+ struct hostapd_data *tmp_hapd;
if (!hostapd_is_mld_ap(hapd))
return;
- for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- struct mld_link_info *link = &sta->mld_info.links[link_id];
+ for_each_mld_link(tmp_hapd, hapd) {
+ struct mld_link_info *link;
+ struct sta_info *tmp_sta;
+ if (tmp_hapd == hapd)
+ continue;
+
+ link = &sta->mld_info.links[tmp_hapd->mld_link_id];
if (!link->valid)
continue;
- for (i = 0; i < hapd->iface->interfaces->count; i++) {
- struct sta_info *tmp_sta;
- struct hostapd_data *tmp_hapd =
- hapd->iface->interfaces->iface[i]->bss[0];
-
- if (!hostapd_is_ml_partner(tmp_hapd, hapd))
+ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+ tmp_sta = tmp_sta->next) {
+ if (tmp_sta == sta ||
+ tmp_sta->mld_assoc_link_id !=
+ sta->mld_assoc_link_id ||
+ tmp_sta->aid != sta->aid)
continue;
- for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
- tmp_sta = tmp_sta->next) {
- if (tmp_sta == sta ||
- tmp_sta->mld_assoc_link_id !=
- sta->mld_assoc_link_id ||
- tmp_sta->aid != sta->aid)
- continue;
-
- ieee80211_ml_link_sta_assoc_cb(tmp_hapd,
- tmp_sta, link,
- ok);
- break;
- }
+ ieee80211_ml_link_sta_assoc_cb(tmp_hapd, tmp_sta, link,
+ ok);
+ break;
}
}
#endif /* CONFIG_IEEE80211BE */
@@ -6622,6 +6657,7 @@
if ((sta->flags & WLAN_STA_WDS) ||
(sta->flags & WLAN_STA_MULTI_AP &&
(hapd->conf->multi_ap & BACKHAUL_BSS) &&
+ hapd->conf->wds_sta &&
!(sta->flags & WLAN_STA_WPS))) {
int ret;
char ifname_wds[IFNAMSIZ + 1];
@@ -6914,33 +6950,6 @@
}
-void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
- const u8 *data, size_t len, int ack)
-{
- struct sta_info *sta;
- struct hostapd_iface *iface = hapd->iface;
-
- sta = ap_get_sta(hapd, dst);
- if (sta == NULL && iface->num_bss > 1) {
- size_t j;
- for (j = 0; j < iface->num_bss; j++) {
- hapd = iface->bss[j];
- sta = ap_get_sta(hapd, dst);
- if (sta)
- break;
- }
- }
- if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
- wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
- MACSTR " that is not currently associated",
- MAC2STR(dst));
- return;
- }
-
- ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
-}
-
-
void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta;
@@ -7326,24 +7335,61 @@
u8 end;
};
+static bool hostapd_skip_rnr(size_t i, struct mbssid_ie_profiles *skip_profiles,
+ bool ap_mld, u8 tbtt_info_len, bool mld_update,
+ struct hostapd_data *reporting_hapd,
+ struct hostapd_data *bss)
+{
+ if (skip_profiles &&
+ i >= skip_profiles->start && i < skip_profiles->end)
+ return true;
+
+ /* No need to report if length is for normal TBTT and the BSS is
+ * affiliated with an AP MLD. MLD TBTT will include this. */
+ if (tbtt_info_len == RNR_TBTT_INFO_LEN && ap_mld)
+ return true;
+
+ /* No need to report if length is for MLD TBTT and the BSS is not
+ * affiliated with an aP MLD. Normal TBTT will include this. */
+ if (tbtt_info_len == RNR_TBTT_INFO_MLD_LEN && !ap_mld)
+ return true;
+
+#ifdef CONFIG_IEEE80211BE
+ /* If building for co-location and they are ML partners, no need to
+ * include since the ML RNR will carry this. */
+ if (!mld_update && hostapd_is_ml_partner(reporting_hapd, bss))
+ return true;
+
+ /* If building for ML RNR and they are not ML partners, don't include.
+ */
+ if (mld_update && !hostapd_is_ml_partner(reporting_hapd, bss))
+ return true;
+#endif /* CONFIG_IEEE80211BE */
+
+ return false;
+}
+
+
static size_t
hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
struct hostapd_data *reporting_hapd,
size_t *current_len,
- struct mbssid_ie_profiles *skip_profiles)
+ struct mbssid_ie_profiles *skip_profiles,
+ bool mld_update)
{
size_t total_len = 0, len = *current_len;
- int tbtt_count = 0;
- size_t i, start = 0;
- bool ap_mld = false;
+ int tbtt_count, total_tbtt_count = 0;
+ size_t i, start;
+ u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN :
+ RNR_TBTT_INFO_LEN;
-#ifdef CONFIG_IEEE80211BE
- ap_mld = !!hapd->conf->mld_ap;
-#endif /* CONFIG_IEEE80211BE */
+repeat_rnr_len:
+ start = 0;
+ tbtt_count = 0;
while (start < hapd->iface->num_bss) {
if (!len ||
- len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
+ len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
len = RNR_HEADER_LEN;
total_len += RNR_HEADER_LEN;
@@ -7355,35 +7401,61 @@
for (i = start; i < hapd->iface->num_bss; i++) {
struct hostapd_data *bss = hapd->iface->bss[i];
+ bool ap_mld = false;
if (!bss || !bss->conf || !bss->started)
continue;
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = bss->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
if (bss == reporting_hapd ||
bss->conf->ignore_broadcast_ssid)
continue;
- if (skip_profiles &&
- i >= skip_profiles->start && i < skip_profiles->end)
+ if (hostapd_skip_rnr(i, skip_profiles, ap_mld,
+ tbtt_info_len, mld_update,
+ reporting_hapd, bss))
continue;
- if (len + RNR_TBTT_INFO_LEN > 255 ||
+ if (len + tbtt_info_len > 255 ||
tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
break;
- if (!ap_mld) {
- len += RNR_TBTT_INFO_LEN;
- total_len += RNR_TBTT_INFO_LEN;
- } else {
- len += RNR_TBTT_INFO_MLD_LEN;
- total_len += RNR_TBTT_INFO_MLD_LEN;
- }
+ len += tbtt_info_len;
+ total_len += tbtt_info_len;
tbtt_count++;
}
start = i;
}
- if (!tbtt_count)
+ total_tbtt_count += tbtt_count;
+
+ /* If building for co-location, re-build again but this time include
+ * ML TBTTs.
+ */
+ if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
+ tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
+
+ /* If no TBTT was found, adjust the len and total_len since it
+ * would have incremented before we checked all BSSs. */
+ if (!tbtt_count) {
+ len -= RNR_TBTT_HEADER_LEN;
+ total_len -= RNR_TBTT_HEADER_LEN;
+ }
+
+ goto repeat_rnr_len;
+ }
+
+ /* This is possible when in the re-built case and no suitable TBTT was
+ * found. Adjust the length accordingly. */
+ if (!tbtt_count && total_tbtt_count) {
+ len -= RNR_TBTT_HEADER_LEN;
+ total_len -= RNR_TBTT_HEADER_LEN;
+ }
+
+ if (!total_tbtt_count)
total_len = 0;
else
*current_len = len;
@@ -7432,8 +7504,8 @@
}
-static size_t hostapd_eid_rnr_multi_iface_len(struct hostapd_data *hapd,
- size_t *current_len)
+static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd,
+ size_t *current_len)
{
struct hostapd_iface *iface;
size_t len = 0;
@@ -7444,66 +7516,89 @@
for (i = 0; i < hapd->iface->interfaces->count; i++) {
iface = hapd->iface->interfaces->iface[i];
- bool ap_mld = false;
-#ifdef CONFIG_IEEE80211BE
- if (hostapd_is_ml_partner(hapd, iface->bss[0]))
- ap_mld = true;
-#endif /* CONFIG_IEEE80211BE */
-
- if (iface == hapd->iface ||
+ if (!iface || iface == hapd->iface ||
iface->state != HAPD_IFACE_ENABLED ||
- !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
+ !is_6ghz_op_class(iface->conf->op_class))
continue;
len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
- current_len, NULL);
+ current_len, NULL, false);
}
return len;
}
-size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
+static size_t hostapd_eid_rnr_mlo_len(struct hostapd_data *hapd, u32 type,
+ size_t *current_len)
+{
+ size_t len = 0;
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_iface *iface;
+ size_t i;
+
+ if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap)
+ return 0;
+
+ /* TODO: Allow for FILS/Action as well */
+ if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
+ return 0;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ iface = hapd->iface->interfaces->iface[i];
+
+ if (!iface || iface == hapd->iface ||
+ hapd->iface->freq == iface->freq)
+ continue;
+
+ len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
+ current_len, NULL, true);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return len;
+}
+
+
+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type,
+ bool include_mld_params)
{
size_t total_len = 0, current_len = 0;
enum colocation_mode mode = get_colocation_mode(hapd);
- bool ap_mld = false;
-
-#ifdef CONFIG_IEEE80211BE
- ap_mld = !!hapd->conf->mld_ap;
-#endif /* CONFIG_IEEE80211BE */
switch (type) {
case WLAN_FC_STYPE_BEACON:
if (hapd->conf->rnr)
total_len += hostapd_eid_nr_db_len(hapd, ¤t_len);
/* fallthrough */
-
case WLAN_FC_STYPE_PROBE_RESP:
- if (mode == COLOCATED_LOWER_BAND || ap_mld)
+ if (mode == COLOCATED_LOWER_BAND)
total_len +=
- hostapd_eid_rnr_multi_iface_len(hapd,
- ¤t_len);
+ hostapd_eid_rnr_colocation_len(hapd,
+ ¤t_len);
if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
!hapd->iconf->mbssid)
total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
¤t_len,
- NULL);
+ NULL, false);
break;
-
case WLAN_FC_STYPE_ACTION:
if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
¤t_len,
- NULL);
- break;
-
- default:
+ NULL, false);
break;
}
+ /* For EMA Beacons, MLD neighbor repoting is added as part of
+ * MBSSID RNR. */
+ if (include_mld_params &&
+ (type != WLAN_FC_STYPE_BEACON ||
+ hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
+ total_len += hostapd_eid_rnr_mlo_len(hapd, type, ¤t_len);
+
return total_len;
}
@@ -7567,7 +7662,8 @@
struct hostapd_data *reporting_hapd,
struct mbssid_ie_profiles *skip_profiles,
size_t i, u8 *tbtt_count, size_t *len,
- u8 **pos)
+ u8 **pos, u8 **tbtt_count_pos, u8 tbtt_info_len,
+ u8 op_class, bool mld_update)
{
struct hostapd_iface *iface = hapd->iface;
struct hostapd_data *bss = iface->bss[i];
@@ -7583,14 +7679,24 @@
bss == reporting_hapd || bss->conf->ignore_broadcast_ssid)
return false;
- if (skip_profiles
- && i >= skip_profiles->start && i < skip_profiles->end)
- return false;
+ if (hostapd_skip_rnr(i, skip_profiles, ap_mld, tbtt_info_len,
+ mld_update, reporting_hapd, bss))
+ return false;
if (*len + RNR_TBTT_INFO_LEN > 255 ||
*tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
return true;
+ if (!(*tbtt_count)) {
+ /* Add neighbor report header info only if there is at least
+ * one TBTT info available. */
+ *tbtt_count_pos = eid++;
+ *eid++ = tbtt_info_len;
+ *eid++ = op_class;
+ *eid++ = bss->iconf->channel;
+ *len += RNR_TBTT_HEADER_LEN;
+ }
+
*eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
os_memcpy(eid, bss->own_addr, ETH_ALEN);
eid += ETH_ALEN;
@@ -7614,29 +7720,34 @@
*eid++ = bss_param;
*eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
- if (!ap_mld) {
- *len += RNR_TBTT_INFO_LEN;
- } else {
#ifdef CONFIG_IEEE80211BE
- u8 param_ch = hapd->eht_mld_bss_param_change;
+ if (ap_mld) {
+ u8 param_ch = bss->eht_mld_bss_param_change;
+ bool is_partner;
- if (hostapd_is_ml_partner(bss, reporting_hapd))
- *eid++ = 0;
- else
- *eid++ = hostapd_get_mld_id(hapd);
-
- *eid++ = hapd->mld_link_id | ((param_ch & 0xF) << 4);
- *eid = (param_ch >> 4) & 0xF;
+ /* If BSS is not a partner of the reporting_hapd
+ * a) MLD ID advertised shall be 255.
+ * b) Link ID advertised shall be 15.
+ * c) BPCC advertised shall be 255 */
+ is_partner = hostapd_is_ml_partner(bss, reporting_hapd);
+ /* MLD ID */
+ *eid++ = is_partner ? hostapd_get_mld_id(bss) : 0xFF;
+ /* Link ID (Bit 3 to Bit 0)
+ * BPCC (Bit 4 to Bit 7) */
+ *eid++ = is_partner ?
+ bss->mld_link_id | ((param_ch & 0xF) << 4) :
+ (MAX_NUM_MLD_LINKS | 0xF0);
+ /* BPCC (Bit 3 to Bit 0) */
+ *eid = is_partner ? ((param_ch & 0xF0) >> 4) : 0x0F;
#ifdef CONFIG_TESTING_OPTIONS
- if (hapd->conf->mld_indicate_disabled)
+ if (bss->conf->mld_indicate_disabled)
*eid |= RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
#endif /* CONFIG_TESTING_OPTIONS */
eid++;
-
- *len += RNR_TBTT_INFO_MLD_LEN;
-#endif /* CONFIG_IEEE80211BE */
}
+#endif /* CONFIG_IEEE80211BE */
+ *len += tbtt_info_len;
(*tbtt_count)++;
*pos = eid;
@@ -7647,18 +7758,17 @@
static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
struct hostapd_data *reporting_hapd,
u8 *eid, size_t *current_len,
- struct mbssid_ie_profiles *skip_profiles)
+ struct mbssid_ie_profiles *skip_profiles,
+ bool mld_update)
{
struct hostapd_iface *iface = hapd->iface;
- size_t i, start = 0;
+ size_t i, start;
size_t len = *current_len;
- u8 *tbtt_count_pos, *eid_start = eid, *size_offset = (eid - len) + 1;
- u8 tbtt_count = 0, op_class, channel;
- bool ap_mld = false;
-
-#ifdef CONFIG_IEEE80211BE
- ap_mld = !!hapd->conf->mld_ap;
-#endif /* CONFIG_IEEE80211BE */
+ u8 *eid_start = eid, *size_offset = (eid - len) + 1;
+ u8 *tbtt_count_pos = size_offset + 1;
+ u8 tbtt_count, total_tbtt_count = 0, op_class, channel;
+ u8 tbtt_info_len = mld_update ? RNR_TBTT_INFO_MLD_LEN :
+ RNR_TBTT_INFO_LEN;
if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq)
return eid;
@@ -7670,9 +7780,12 @@
NUM_HOSTAPD_MODES)
return eid;
+repeat_rnr:
+ start = 0;
+ tbtt_count = 0;
while (start < iface->num_bss) {
if (!len ||
- len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255 ||
+ len + RNR_TBTT_HEADER_LEN + tbtt_info_len > 255 ||
tbtt_count >= RNR_TBTT_INFO_COUNT_MAX) {
eid_start = eid;
*eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
@@ -7681,25 +7794,34 @@
tbtt_count = 0;
}
- tbtt_count_pos = eid++;
- *eid++ = ap_mld ? RNR_TBTT_INFO_MLD_LEN : RNR_TBTT_INFO_LEN;
- *eid++ = op_class;
- *eid++ = hapd->iconf->channel;
- len += RNR_TBTT_HEADER_LEN;
-
for (i = start; i < iface->num_bss; i++) {
if (hostapd_eid_rnr_bss(hapd, reporting_hapd,
skip_profiles, i,
- &tbtt_count, &len, &eid))
+ &tbtt_count, &len, &eid,
+ &tbtt_count_pos, tbtt_info_len,
+ op_class, mld_update))
break;
}
start = i;
- *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
- *size_offset = (eid - size_offset) - 1;
+
+ if (tbtt_count) {
+ *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
+ *size_offset = (eid - size_offset) - 1;
+ }
}
- if (tbtt_count == 0)
+ total_tbtt_count += tbtt_count;
+
+ /* If building for co-location, re-build again but this time include
+ * ML TBTTs.
+ */
+ if (!mld_update && tbtt_info_len == RNR_TBTT_INFO_LEN) {
+ tbtt_info_len = RNR_TBTT_INFO_MLD_LEN;
+ goto repeat_rnr;
+ }
+
+ if (!total_tbtt_count)
return eid_start;
*current_len = len;
@@ -7707,8 +7829,8 @@
}
-static u8 * hostapd_eid_rnr_multi_iface(struct hostapd_data *hapd, u8 *eid,
- size_t *current_len)
+u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
+ size_t *current_len)
{
struct hostapd_iface *iface;
size_t i;
@@ -7718,64 +7840,88 @@
for (i = 0; i < hapd->iface->interfaces->count; i++) {
iface = hapd->iface->interfaces->iface[i];
- bool ap_mld = false;
-#ifdef CONFIG_IEEE80211BE
- if (hostapd_is_ml_partner(hapd, iface->bss[0]))
- ap_mld = true;
-#endif /* CONFIG_IEEE80211BE */
-
- if (iface == hapd->iface ||
+ if (!iface || iface == hapd->iface ||
iface->state != HAPD_IFACE_ENABLED ||
- !(is_6ghz_op_class(iface->conf->op_class) || ap_mld))
+ !is_6ghz_op_class(iface->conf->op_class))
continue;
eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
- current_len, NULL);
+ current_len, NULL, false);
}
return eid;
}
-u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
+u8 * hostapd_eid_rnr_mlo(struct hostapd_data *hapd, u32 type,
+ u8 *eid, size_t *current_len)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_iface *iface;
+ size_t i;
+
+ if (!hapd->iface || !hapd->iface->interfaces || !hapd->conf->mld_ap)
+ return eid;
+
+ /* TODO: Allow for FILS/Action as well */
+ if (type != WLAN_FC_STYPE_BEACON && type != WLAN_FC_STYPE_PROBE_RESP)
+ return eid;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ iface = hapd->iface->interfaces->iface[i];
+
+ if (!iface || iface == hapd->iface ||
+ hapd->iface->freq == iface->freq)
+ continue;
+
+ eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
+ current_len, NULL, true);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return eid;
+}
+
+
+u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type,
+ bool include_mld_params)
{
u8 *eid_start = eid;
size_t current_len = 0;
enum colocation_mode mode = get_colocation_mode(hapd);
- bool ap_mld = false;
-
-#ifdef CONFIG_IEEE80211BE
- ap_mld = !!hapd->conf->mld_ap;
-#endif /* CONFIG_IEEE80211BE */
switch (type) {
case WLAN_FC_STYPE_BEACON:
if (hapd->conf->rnr)
eid = hostapd_eid_nr_db(hapd, eid, ¤t_len);
/* fallthrough */
-
case WLAN_FC_STYPE_PROBE_RESP:
- if (mode == COLOCATED_LOWER_BAND || ap_mld)
- eid = hostapd_eid_rnr_multi_iface(hapd, eid,
- ¤t_len);
+ if (mode == COLOCATED_LOWER_BAND)
+ eid = hostapd_eid_rnr_colocation(hapd, eid,
+ ¤t_len);
if (hapd->conf->rnr && hapd->iface->num_bss > 1 &&
!hapd->iconf->mbssid)
eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
- ¤t_len, NULL);
+ ¤t_len, NULL, false);
break;
-
case WLAN_FC_STYPE_ACTION:
if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
- ¤t_len, NULL);
+ ¤t_len, NULL, false);
break;
-
default:
return eid_start;
}
+ /* For EMA Beacons, MLD neighbor repoting is added as part of
+ * MBSSID RNR. */
+ if (include_mld_params &&
+ (type != WLAN_FC_STYPE_BEACON ||
+ hapd->iconf->mbssid != ENHANCED_MBSSID_ENABLED))
+ eid = hostapd_eid_rnr_mlo(hapd, type, eid, ¤t_len);
+
if (eid == eid_start + 2)
return eid_start;
@@ -7900,6 +8046,11 @@
size_t known_bss_len, size_t *rnr_len)
{
size_t len = 0, bss_index = 1;
+ bool ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
(frame_type != WLAN_FC_STYPE_BEACON &&
@@ -7932,12 +8083,12 @@
*rnr_len += hostapd_eid_rnr_iface_len(
hapd, hostapd_mbssid_get_tx_bss(hapd),
- &rnr_cur_len, &skip_profiles);
+ &rnr_cur_len, &skip_profiles, ap_mld);
}
}
if (hapd->iconf->mbssid == ENHANCED_MBSSID_ENABLED && rnr_len)
- *rnr_len += hostapd_eid_rnr_len(hapd, frame_type);
+ *rnr_len += hostapd_eid_rnr_len(hapd, frame_type, false);
return len;
}
@@ -8066,7 +8217,11 @@
{
size_t bss_index = 1, cur_len = 0;
u8 elem_index = 0, *rnr_start_eid = rnr_eid;
- bool add_rnr;
+ bool add_rnr, ap_mld = false;
+
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
if (!hapd->iconf->mbssid || hapd->iface->num_bss <= 1 ||
(frame_stype != WLAN_FC_STYPE_BEACON &&
@@ -8111,7 +8266,7 @@
cur_len = 0;
rnr_eid = hostapd_eid_rnr_iface(
hapd, hostapd_mbssid_get_tx_bss(hapd),
- rnr_eid, &cur_len, &skip_profiles);
+ rnr_eid, &cur_len, &skip_profiles, ap_mld);
}
}
@@ -8123,8 +8278,8 @@
if (hapd->conf->rnr)
rnr_eid = hostapd_eid_nr_db(hapd, rnr_eid, &cur_len);
if (get_colocation_mode(hapd) == COLOCATED_LOWER_BAND)
- rnr_eid = hostapd_eid_rnr_multi_iface(hapd, rnr_eid,
- &cur_len);
+ rnr_eid = hostapd_eid_rnr_colocation(hapd, rnr_eid,
+ &cur_len);
}
return eid;
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index a35486d..dd4995f 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -129,11 +129,10 @@
const u8 *he_6ghz_capab);
int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
enum ieee80211_op_mode mode);
+bool hostapd_get_ht_vht_twt_responder(struct hostapd_data *hapd);
u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid);
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
const u8 *buf, size_t len, int ack);
-void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
- const u8 *data, size_t len, int ack);
void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
int wds);
u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
@@ -148,7 +147,8 @@
u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
int hostapd_update_time_adv(struct hostapd_data *hapd);
void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
-u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid,
+ u16 value);
int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
#ifdef CONFIG_SAE
@@ -227,8 +227,10 @@
u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len);
u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ext_capab_ie, size_t ext_capab_ie_len);
-size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type);
-u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type);
+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type,
+ bool include_mld_params);
+u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type,
+ bool include_mld_params);
int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
int res, struct radius_sta *info);
size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index e723ae7..913a995 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -128,6 +128,9 @@
goto fail;
}
+ if (!radius_msg_add_msg_auth(msg))
+ goto fail;
+
os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
os_strlen(buf))) {
@@ -505,7 +508,9 @@
"Found matching Access-Request for RADIUS message (id=%d)",
query->radius_id);
- if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
+ if (radius_msg_verify(
+ msg, shared_secret, shared_secret_len, req,
+ hapd->conf->radius_require_message_authenticator)) {
wpa_printf(MSG_INFO,
"Incoming RADIUS packet did not have correct authenticator - dropped");
return RADIUS_RX_INVALID_AUTHENTICATOR;
@@ -596,7 +601,8 @@
if (query->radius_psk) {
struct sta_info *sta;
- bool success = cache->accepted == HOSTAPD_ACL_ACCEPT;
+ bool success = cache->accepted == HOSTAPD_ACL_ACCEPT ||
+ cache->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT;
sta = ap_get_sta(hapd, query->addr);
if (!sta || !sta->wpa_sm) {
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index 638546e..afb2e16 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -1030,7 +1030,7 @@
static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
struct sta_info *sta)
{
- u8 i, link_id;
+ u8 link_id;
struct mld_info *info = &sta->mld_info;
if (!ap_sta_is_mld(hapd, sta)) {
@@ -1050,31 +1050,18 @@
for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
struct hostapd_data *other_hapd;
- if (!info->links[link_id].valid)
+ if (!info->links[link_id].valid || link_id == hapd->mld_link_id)
continue;
- for (i = 0; i < hapd->iface->interfaces->count; i++) {
- other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
-
- if (hapd == other_hapd)
- continue;
-
- if (hostapd_is_ml_partner(hapd, other_hapd) &&
- link_id == other_hapd->mld_link_id)
- break;
- }
-
- if (i == hapd->iface->interfaces->count &&
- link_id != hapd->mld_link_id) {
+ other_hapd = hostapd_mld_get_link_bss(hapd, link_id);
+ if (!other_hapd) {
wpa_printf(MSG_DEBUG, "MLD: Invalid link ID=%u",
link_id);
return -1;
}
- if (i < hapd->iface->interfaces->count)
- os_memcpy(info->links[link_id].local_addr,
- other_hapd->own_addr,
- ETH_ALEN);
+ os_memcpy(info->links[link_id].local_addr, other_hapd->own_addr,
+ ETH_ALEN);
}
return 0;
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 85790c7..3dd3a6a 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -441,6 +441,8 @@
if (hostapd_get_he_twt_responder(hapd, IEEE80211_MODE_AP))
*pos |= 0x40; /* Bit 78 - TWT responder */
#endif /* CONFIG_IEEE80211AX */
+ if (hostapd_get_ht_vht_twt_responder(hapd))
+ *pos |= 0x40; /* Bit 78 - TWT responder */
break;
case 10: /* Bits 80-87 */
#ifdef CONFIG_SAE
@@ -735,12 +737,14 @@
}
-u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
+u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid,
+ u16 value)
{
u8 *pos = eid;
#ifdef CONFIG_WNM_AP
- if (hapd->conf->ap_max_inactivity > 0) {
+ if (hapd->conf->ap_max_inactivity > 0 &&
+ hapd->conf->bss_max_idle) {
unsigned int val;
*pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD;
*pos++ = 3;
@@ -753,9 +757,13 @@
val = 1;
if (val > 65535)
val = 65535;
+ if (value)
+ val = value;
WPA_PUT_LE16(pos, val);
pos += 2;
- *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */
+ /* Set the Protected Keep-Alive Required bit based on
+ * configuration */
+ *pos++ = hapd->conf->bss_max_idle == 2 ? BIT(0) : 0x00;
}
#endif /* CONFIG_WNM_AP */
@@ -1089,7 +1097,7 @@
{
u8 *pos = eid;
bool sae_pk = false;
- u16 capab = 0;
+ u32 capab = 0, tmp;
size_t flen;
if (!(hapd->conf->wpa & WPA_PROTO_RSN))
@@ -1118,18 +1126,28 @@
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP)
capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+ if (hapd->conf->ssid_protection)
+ capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
- flen = (capab & 0xff00) ? 2 : 1;
- if (len < 2 + flen || !capab)
+ if (!capab)
+ return eid; /* no supported extended RSN capabilities */
+ tmp = capab;
+ flen = 0;
+ while (tmp) {
+ flen++;
+ tmp >>= 8;
+ }
+
+ if (len < 2 + flen)
return eid; /* no supported extended RSN capabilities */
capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
*pos++ = WLAN_EID_RSNX;
*pos++ = flen;
- *pos++ = capab & 0x00ff;
- capab >>= 8;
- if (capab)
- *pos++ = capab;
+ while (capab) {
+ *pos++ = capab & 0xff;
+ capab >>= 8;
+ }
return pos;
}
@@ -1198,3 +1216,13 @@
return sta;
}
+
+
+bool hostapd_get_ht_vht_twt_responder(struct hostapd_data *hapd)
+{
+ return hapd->iconf->ht_vht_twt_responder &&
+ ((hapd->iconf->ieee80211n && !hapd->conf->disable_11n) ||
+ (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac)) &&
+ (hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_HT_VHT_TWT_RESPONDER);
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 8e98b65..f4103ac 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -767,6 +767,9 @@
goto fail;
}
+ if (!radius_msg_add_msg_auth(msg))
+ goto fail;
+
if (sm->identity &&
!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
sm->identity, sm->identity_len)) {
@@ -2039,16 +2042,7 @@
}
sta = sm->sta;
- /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be
- * present when packet contains an EAP-Message attribute */
- if (hdr->code == RADIUS_CODE_ACCESS_REJECT &&
- radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
- 0) < 0 &&
- radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
- wpa_printf(MSG_DEBUG,
- "Allowing RADIUS Access-Reject without Message-Authenticator since it does not include EAP-Message");
- } else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
- req, 1)) {
+ if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 1)) {
wpa_printf(MSG_INFO,
"Incoming RADIUS packet did not have correct Message-Authenticator - dropped");
return RADIUS_RX_INVALID_AUTHENTICATOR;
@@ -2543,12 +2537,21 @@
if (!hostapd_mld_is_first_bss(hapd)) {
struct hostapd_data *first;
- wpa_printf(MSG_DEBUG,
- "MLD: Using IEEE 802.1X state machine of the first BSS");
-
first = hostapd_mld_get_first_bss(hapd);
if (!first)
return -1;
+
+ if (!first->eapol_auth) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: First BSS IEEE 802.1X state machine does not exist. Init on its behalf");
+
+ if (ieee802_1x_init(first))
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Using IEEE 802.1X state machine of the first BSS");
+
hapd->eapol_auth = first->eapol_auth;
return 0;
}
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 32944ed..13613db 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -200,6 +200,28 @@
}
+#ifdef CONFIG_IEEE80211BE
+static void clear_wpa_sm_for_each_partner_link(struct hostapd_data *hapd,
+ struct sta_info *psta)
+{
+ struct sta_info *lsta;
+ struct hostapd_data *lhapd;
+
+ if (!ap_sta_is_mld(hapd, psta))
+ return;
+
+ for_each_mld_link(lhapd, hapd) {
+ if (lhapd == hapd)
+ continue;
+
+ lsta = ap_get_sta(lhapd, psta->addr);
+ if (lsta)
+ lsta->wpa_sm = NULL;
+ }
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
{
int set_beacon = 0;
@@ -213,6 +235,7 @@
if ((sta->flags & WLAN_STA_WDS) ||
(sta->flags & WLAN_STA_MULTI_AP &&
(hapd->conf->multi_ap & BACKHAUL_BSS) &&
+ hapd->conf->wds_sta &&
!(sta->flags & WLAN_STA_WPS)))
hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
@@ -317,9 +340,17 @@
#ifdef CONFIG_IEEE80211BE
if (!ap_sta_is_mld(hapd, sta) ||
- hapd->mld_link_id == sta->mld_assoc_link_id)
+ hapd->mld_link_id == sta->mld_assoc_link_id) {
wpa_auth_sta_deinit(sta->wpa_sm);
-#else
+ /* Remove references from partner links. */
+ clear_wpa_sm_for_each_partner_link(hapd, sta);
+ }
+
+ /* Release group references in case non-association link STA is removed
+ * before association link STA */
+ if (hostapd_sta_is_link_sta(hapd, sta))
+ wpa_release_link_auth_ref(sta->wpa_sm, hapd->mld_link_id);
+#else /* CONFIG_IEEE80211BE */
wpa_auth_sta_deinit(sta->wpa_sm);
#endif /* CONFIG_IEEE80211BE */
@@ -502,6 +533,7 @@
struct sta_info *sta = timeout_ctx;
unsigned long next_time = 0;
int reason;
+ int max_inactivity = hapd->conf->ap_max_inactivity;
wpa_printf(MSG_DEBUG, "%s: %s: " MACSTR " flags=0x%x timeout_next=%d",
hapd->conf->iface, __func__, MAC2STR(sta->addr), sta->flags,
@@ -514,6 +546,9 @@
return;
}
+ if (sta->max_idle_period)
+ max_inactivity = (sta->max_idle_period * 1024 + 999) / 1000;
+
if ((sta->flags & WLAN_STA_ASSOC) &&
(sta->timeout_next == STA_NULLFUNC ||
sta->timeout_next == STA_DISASSOC)) {
@@ -535,7 +570,7 @@
* Anyway, try again after the next inactivity timeout,
* but do not disconnect the station now.
*/
- next_time = hapd->conf->ap_max_inactivity + fuzz;
+ next_time = max_inactivity + fuzz;
} else if (inactive_sec == -ENOENT) {
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Station " MACSTR " has lost its driver entry",
@@ -544,20 +579,19 @@
/* Avoid sending client probe on removed client */
sta->timeout_next = STA_DISASSOC;
goto skip_poll;
- } else if (inactive_sec < hapd->conf->ap_max_inactivity) {
+ } else if (inactive_sec < max_inactivity) {
/* station activity detected; reset timeout state */
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Station " MACSTR " has been active %is ago",
MAC2STR(sta->addr), inactive_sec);
sta->timeout_next = STA_NULLFUNC;
- next_time = hapd->conf->ap_max_inactivity + fuzz -
- inactive_sec;
+ next_time = max_inactivity + fuzz - inactive_sec;
} else {
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Station " MACSTR " has been "
"inactive too long: %d sec, max allowed: %d",
MAC2STR(sta->addr), inactive_sec,
- hapd->conf->ap_max_inactivity);
+ max_inactivity);
if (hapd->conf->skip_inactivity_poll)
sta->timeout_next = STA_DISASSOC;
@@ -573,7 +607,7 @@
/* data nullfunc frame poll did not produce TX errors; assume
* station ACKed it */
sta->timeout_next = STA_NULLFUNC;
- next_time = hapd->conf->ap_max_inactivity;
+ next_time = max_inactivity;
}
skip_poll:
@@ -761,6 +795,7 @@
{
struct sta_info *sta;
int i;
+ int max_inactivity = hapd->conf->ap_max_inactivity;
sta = ap_get_sta(hapd, addr);
if (sta)
@@ -794,12 +829,15 @@
}
sta->supported_rates_len = i;
+ if (sta->max_idle_period)
+ max_inactivity = (sta->max_idle_period * 1024 + 999) / 1000;
+
if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
"for " MACSTR " (%d seconds - ap_max_inactivity)",
__func__, MAC2STR(addr),
- hapd->conf->ap_max_inactivity);
- eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+ max_inactivity);
+ eloop_register_timeout(max_inactivity, 0,
ap_handle_timer, hapd, sta);
}
@@ -903,9 +941,11 @@
ieee802_1x_free_station(hapd, sta);
#ifdef CONFIG_IEEE80211BE
if (!hapd->conf->mld_ap ||
- hapd->mld_link_id == sta->mld_assoc_link_id)
+ hapd->mld_link_id == sta->mld_assoc_link_id) {
wpa_auth_sta_deinit(sta->wpa_sm);
-#else
+ clear_wpa_sm_for_each_partner_link(hapd, sta);
+ }
+#else /* CONFIG_IEEE80211BE */
wpa_auth_sta_deinit(sta->wpa_sm);
#endif /* CONFIG_IEEE80211BE */
@@ -1761,10 +1801,8 @@
struct sta_info *sta)
{
struct hostapd_data *tmp_hapd;
- unsigned int i, j;
- for_each_mld_link(tmp_hapd, i, j, hapd->iface->interfaces,
- hostapd_get_mld_id(hapd)) {
+ for_each_mld_link(tmp_hapd, hapd) {
struct sta_info *tmp_sta;
if (hapd == tmp_hapd)
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 153e4a0..8462935 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -94,8 +94,6 @@
u16 status;
u16 resp_sta_profile_len;
u8 *resp_sta_profile;
-
- const u8 *rsne, *rsnxe;
} links[MAX_NUM_MLD_LINKS];
};
@@ -334,6 +332,9 @@
struct mld_info mld_info;
u8 mld_assoc_link_id;
#endif /* CONFIG_IEEE80211BE */
+
+ u16 max_idle_period; /* if nonzero, the granted BSS max idle period in
+ * units of 1000 TUs */
};
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 4bf8d79..bbf41d3 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -62,7 +62,7 @@
const u8 *pmk, unsigned int pmk_len,
struct wpa_ptk *ptk, int force_sha256,
u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
- size_t *key_len);
+ size_t *key_len, bool no_kdk);
static void wpa_group_free(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static void wpa_group_get(struct wpa_authenticator *wpa_auth,
@@ -71,6 +71,9 @@
struct wpa_group *group);
static int ieee80211w_kde_len(struct wpa_state_machine *sm);
static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
+static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group);
+
static const u32 eapol_key_timeout_first = 100; /* ms */
static const u32 eapol_key_timeout_subseq = 1000; /* ms */
@@ -87,7 +90,7 @@
{
#ifdef CONFIG_IEEE80211BE
if (sm->mld_assoc_link_id >= 0)
- return sm->own_mld_addr;
+ return sm->wpa_auth->mld_addr;
#endif /* CONFIG_IEEE80211BE */
return sm->wpa_auth->addr;
}
@@ -103,6 +106,125 @@
}
+static void wpa_gkeydone_sta(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!sm->wpa_auth)
+ return;
+
+ sm->wpa_auth->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = false;
+
+#ifdef CONFIG_IEEE80211BE
+ for_each_sm_auth(sm, link_id)
+ sm->mld_links[link_id].wpa_auth->group->GKeyDoneStations--;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+
+void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
+ int release_link_id)
+{
+ int link_id;
+
+ if (!sm || release_link_id >= MAX_NUM_MLD_LINKS)
+ return;
+
+ for_each_sm_auth(sm, link_id) {
+ if (link_id == release_link_id) {
+ wpa_group_put(sm->mld_links[link_id].wpa_auth,
+ sm->mld_links[link_id].wpa_auth->group);
+ sm->mld_links[link_id].wpa_auth = NULL;
+ }
+ }
+}
+
+
+struct wpa_get_link_auth_ctx {
+ const u8 *addr;
+ const u8 *mld_addr;
+ int link_id;
+ struct wpa_authenticator *wpa_auth;
+};
+
+static int wpa_get_link_sta_auth(struct wpa_authenticator *wpa_auth, void *data)
+{
+ struct wpa_get_link_auth_ctx *ctx = data;
+
+ if (!wpa_auth->is_ml)
+ return 0;
+
+ if (ctx->mld_addr &&
+ !ether_addr_equal(wpa_auth->mld_addr, ctx->mld_addr))
+ return 0;
+
+ if ((ctx->addr && ether_addr_equal(wpa_auth->addr, ctx->addr)) ||
+ (ctx->link_id > -1 && wpa_auth->is_ml &&
+ wpa_auth->link_id == ctx->link_id)) {
+ ctx->wpa_auth = wpa_auth;
+ return 1;
+
+ }
+ return 0;
+}
+
+
+static struct wpa_authenticator *
+wpa_get_link_auth(struct wpa_authenticator *wpa_auth, int link_id)
+{
+ struct wpa_get_link_auth_ctx ctx;
+
+ ctx.addr = NULL;
+ ctx.mld_addr = wpa_auth->mld_addr;
+ ctx.link_id = link_id;
+ ctx.wpa_auth = NULL;
+ wpa_auth_for_each_auth(wpa_auth, wpa_get_link_sta_auth, &ctx);
+ return ctx.wpa_auth;
+}
+
+
+static int wpa_get_primary_auth_cb(struct wpa_authenticator *wpa_auth,
+ void *data)
+{
+ struct wpa_get_link_auth_ctx *ctx = data;
+
+ if (!wpa_auth->is_ml ||
+ !ether_addr_equal(wpa_auth->mld_addr, ctx->addr) ||
+ !wpa_auth->primary_auth)
+ return 0;
+
+ ctx->wpa_auth = wpa_auth;
+ return 1;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
+static struct wpa_authenticator *
+wpa_get_primary_auth(struct wpa_authenticator *wpa_auth)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct wpa_get_link_auth_ctx ctx;
+
+ if (!wpa_auth || !wpa_auth->is_ml || wpa_auth->primary_auth)
+ return wpa_auth;
+
+ ctx.addr = wpa_auth->mld_addr;
+ ctx.wpa_auth = NULL;
+ wpa_auth_for_each_auth(wpa_auth, wpa_get_primary_auth_cb, &ctx);
+
+ return ctx.wpa_auth;
+#else /* CONFIG_IEEE80211BE */
+ return wpa_auth;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
static inline int wpa_auth_mic_failure_report(
struct wpa_authenticator *wpa_auth, const u8 *addr)
{
@@ -372,14 +494,16 @@
}
-static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
+static void wpa_rekey_all_groups(struct wpa_authenticator *wpa_auth)
{
- struct wpa_authenticator *wpa_auth = eloop_ctx;
struct wpa_group *group, *next;
wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
group = wpa_auth->group;
while (group) {
+ wpa_printf(MSG_DEBUG, "GTK rekey start for authenticator ("
+ MACSTR "), group vlan %d",
+ MAC2STR(wpa_auth->addr), group->vlan_id);
wpa_group_get(wpa_auth, group);
group->GTKReKey = true;
@@ -392,6 +516,83 @@
wpa_group_put(wpa_auth, group);
group = next;
}
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+
+static void wpa_update_all_gtks(struct wpa_authenticator *wpa_auth)
+{
+ struct wpa_group *group, *next;
+
+ group = wpa_auth->group;
+ while (group) {
+ wpa_group_get(wpa_auth, group);
+
+ wpa_group_update_gtk(wpa_auth, group);
+ next = group->next;
+ wpa_group_put(wpa_auth, group);
+ group = next;
+ }
+}
+
+
+static int wpa_update_all_gtks_cb(struct wpa_authenticator *wpa_auth, void *ctx)
+{
+ const u8 *mld_addr = ctx;
+
+ if (!ether_addr_equal(wpa_auth->mld_addr, mld_addr))
+ return 0;
+
+ wpa_update_all_gtks(wpa_auth);
+ return 0;
+}
+
+
+static int wpa_rekey_all_groups_cb(struct wpa_authenticator *wpa_auth,
+ void *ctx)
+{
+ const u8 *mld_addr = ctx;
+
+ if (!ether_addr_equal(wpa_auth->mld_addr, mld_addr))
+ return 0;
+
+ wpa_rekey_all_groups(wpa_auth);
+ return 0;
+}
+
+#endif /* CONFIG_IEEE80211BE */
+
+
+static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+
+#ifdef CONFIG_IEEE80211BE
+ if (wpa_auth->is_ml) {
+ /* Non-primary ML authenticator eloop timer for group rekey is
+ * never started and shouldn't fire. Check and warn just in
+ * case. */
+ if (!wpa_auth->primary_auth) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Cannot start GTK rekey on non-primary ML authenticator");
+ return;
+ }
+
+ /* Generate all the new group keys */
+ wpa_auth_for_each_auth(wpa_auth, wpa_update_all_gtks_cb,
+ wpa_auth->mld_addr);
+
+ /* Send all the generated group keys to the respective stations
+ * with group key handshake. */
+ wpa_auth_for_each_auth(wpa_auth, wpa_rekey_all_groups_cb,
+ wpa_auth->mld_addr);
+ } else {
+ wpa_rekey_all_groups(wpa_auth);
+ }
+#else /* CONFIG_IEEE80211BE */
+ wpa_rekey_all_groups(wpa_auth);
+#endif /* CONFIG_IEEE80211BE */
if (wpa_auth->conf.wpa_group_rekey) {
eloop_register_timeout(wpa_auth->conf.wpa_group_rekey,
@@ -541,8 +742,19 @@
wpa_auth = os_zalloc(sizeof(struct wpa_authenticator));
if (!wpa_auth)
return NULL;
+
os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
+
+#ifdef CONFIG_IEEE80211BE
+ if (conf->mld_addr) {
+ wpa_auth->is_ml = true;
+ wpa_auth->link_id = conf->link_id;
+ wpa_auth->primary_auth = !conf->first_link_auth;
+ os_memcpy(wpa_auth->mld_addr, conf->mld_addr, ETH_ALEN);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
wpa_auth->cb = cb;
wpa_auth->cb_ctx = cb_ctx;
@@ -586,7 +798,15 @@
wpa_rekey_gmk, wpa_auth, NULL);
}
+#ifdef CONFIG_IEEE80211BE
+ /* For AP MLD, run group rekey timer only on one link (first) and
+ * whenever it fires do rekey on all associated ML links in one shot.
+ */
+ if ((!wpa_auth->is_ml || !conf->first_link_auth) &&
+ wpa_auth->conf.wpa_group_rekey) {
+#else /* CONFIG_IEEE80211BE */
if (wpa_auth->conf.wpa_group_rekey) {
+#endif /* CONFIG_IEEE80211BE */
eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0,
wpa_rekey_gtk, wpa_auth, NULL);
}
@@ -650,6 +870,9 @@
struct wpa_group *group, *prev;
eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
+
+ /* TODO: Assign ML primary authenticator to next link authenticator and
+ * start rekey timer. */
eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
pmksa_cache_auth_deinit(wpa_auth->pmksa);
@@ -666,6 +889,9 @@
os_free(wpa_auth->wpa_ie);
+ os_free(wpa_auth->rsne_override);
+ os_free(wpa_auth->rsne_override_2);
+ os_free(wpa_auth->rsnxe_override);
group = wpa_auth->group;
while (group) {
@@ -802,6 +1028,10 @@
static void wpa_free_sta_sm(struct wpa_state_machine *sm)
{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_P2P
if (WPA_GET_BE32(sm->ip_addr)) {
wpa_printf(MSG_DEBUG,
@@ -814,10 +1044,8 @@
bitfield_clear(sm->wpa_auth->ip_pool, sm->ip_addr_bit);
}
#endif /* CONFIG_P2P */
- if (sm->GUpdateStationKeys) {
- sm->group->GKeyDoneStations--;
- sm->GUpdateStationKeys = false;
- }
+ if (sm->GUpdateStationKeys)
+ wpa_gkeydone_sta(sm);
#ifdef CONFIG_IEEE80211R_AP
os_free(sm->assoc_resp_ftie);
wpabuf_free(sm->ft_pending_req_ies);
@@ -825,6 +1053,13 @@
os_free(sm->last_rx_eapol_key);
os_free(sm->wpa_ie);
os_free(sm->rsnxe);
+#ifdef CONFIG_IEEE80211BE
+ for_each_sm_auth(sm, link_id) {
+ wpa_group_put(sm->mld_links[link_id].wpa_auth,
+ sm->mld_links[link_id].wpa_auth->group);
+ sm->mld_links[link_id].wpa_auth = NULL;
+ }
+#endif /* CONFIG_IEEE80211BE */
wpa_group_put(sm->wpa_auth, sm->group);
#ifdef CONFIG_DPP2
wpabuf_clear_free(sm->dpp_z);
@@ -842,12 +1077,20 @@
wpa_auth = sm->wpa_auth;
if (wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
+ struct wpa_authenticator *primary_auth = wpa_auth;
+
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
"strict rekeying - force GTK rekey since STA is leaving");
+
+#ifdef CONFIG_IEEE80211BE
+ if (wpa_auth->is_ml && !wpa_auth->primary_auth)
+ primary_auth = wpa_get_primary_auth(wpa_auth);
+#endif /* CONFIG_IEEE80211BE */
+
if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
- wpa_auth, NULL) == -1)
+ primary_auth, NULL) == -1)
eloop_register_timeout(0, 500000, wpa_rekey_gtk,
- wpa_auth, NULL);
+ primary_auth, NULL);
}
eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
@@ -1087,7 +1330,8 @@
}
if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK, 0,
- pmk_r0, pmk_r1, pmk_r0_name, &key_len) < 0)
+ pmk_r0, pmk_r1, pmk_r0_name, &key_len,
+ false) < 0)
break;
if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
@@ -1601,12 +1845,16 @@
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
LOGGER_INFO,
"received EAPOL-Key Request for GTK rekeying");
- eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
+
+ eloop_cancel_timeout(wpa_rekey_gtk,
+ wpa_get_primary_auth(wpa_auth),
+ NULL);
if (wpa_auth_gtk_rekey_in_process(wpa_auth))
wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG,
"skip new GTK rekey - already in process");
else
- wpa_rekey_gtk(wpa_auth, NULL);
+ wpa_rekey_gtk(wpa_get_primary_auth(wpa_auth),
+ NULL);
}
} else {
/* Do not allow the same key replay counter to be reused. */
@@ -2144,8 +2392,7 @@
* Reauthentication cancels the pending group key
* update for this STA.
*/
- sm->group->GKeyDoneStations--;
- sm->GUpdateStationKeys = false;
+ wpa_gkeydone_sta(sm);
sm->PtkGroupInit = true;
}
sm->ReAuthenticationRequest = true;
@@ -2221,8 +2468,7 @@
sm->keycount = 0;
if (sm->GUpdateStationKeys)
- sm->group->GKeyDoneStations--;
- sm->GUpdateStationKeys = false;
+ wpa_gkeydone_sta(sm);
if (sm->wpa == WPA_VERSION_WPA)
sm->PInitAKeys = false;
if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and
@@ -2600,7 +2846,7 @@
"RSN: MLD: Add MAC Address KDE: kde_len=%zu",
kde_len);
wpa_add_kde(buf + kde_len, RSN_KEY_DATA_MAC_ADDR,
- sm->own_mld_addr, ETH_ALEN, NULL, 0);
+ sm->wpa_auth->mld_addr, ETH_ALEN, NULL, 0);
kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
}
#endif /* CONFIG_IEEE80211BE */
@@ -2626,7 +2872,7 @@
const u8 *pmk, unsigned int pmk_len,
struct wpa_ptk *ptk, int force_sha256,
u8 *pmk_r0, u8 *pmk_r1, u8 *pmk_r0_name,
- size_t *key_len)
+ size_t *key_len, bool no_kdk)
{
const u8 *z = NULL;
size_t z_len = 0, kdk_len;
@@ -2634,7 +2880,7 @@
int ret;
if (sm->wpa_auth->conf.force_kdk_derivation ||
- (sm->wpa_auth->conf.secure_ltf &&
+ (!no_kdk && sm->wpa_auth->conf.secure_ltf &&
ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
kdk_len = WPA_KDK_MAX_LEN;
else
@@ -2663,7 +2909,7 @@
}
#ifdef CONFIG_PASN
- if (sm->wpa_auth->conf.secure_ltf &&
+ if (!no_kdk && sm->wpa_auth->conf.secure_ltf &&
ieee802_11_rsnx_capab(sm->rsnxe,
WLAN_RSNX_CAPAB_SECURE_LTF)) {
ret = wpa_ltf_keyseed(ptk, sm->wpa_key_mgmt,
@@ -2699,7 +2945,7 @@
}
#ifdef CONFIG_PASN
- if (sm->wpa_auth->conf.secure_ltf &&
+ if (!no_kdk && sm->wpa_auth->conf.secure_ltf &&
ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) {
ret = wpa_ltf_keyseed(ptk, sm->wpa_key_mgmt, sm->pairwise);
if (ret) {
@@ -3410,6 +3656,7 @@
size_t key_len;
u8 *key_data_buf = NULL;
size_t key_data_buf_len = 0;
+ bool derive_kdk, no_kdk = false;
SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
sm->EAPOLKeyReceived = false;
@@ -3418,6 +3665,9 @@
mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+ derive_kdk = sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF);
+
/* WPA with IEEE 802.1X: use the derived PMK from EAP
* WPA-PSK: iterate through possible PSKs and select the one matching
* the packet */
@@ -3447,9 +3697,11 @@
pmk_len = sm->pmksa->pmk_len;
}
+ no_kdk = false;
+ try_without_kdk:
if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK,
owe_ptk_workaround == 2, pmk_r0, pmk_r1,
- pmk_r0_name, &key_len) < 0)
+ pmk_r0_name, &key_len, no_kdk) < 0)
break;
if (mic_len &&
@@ -3483,11 +3735,31 @@
}
#endif /* CONFIG_OWE */
+ /* Some deployed STAs that advertise SecureLTF support in the
+ * RSNXE in (Re)Association Request frames, do not derive KDK
+ * during PTK generation. Try to work around this by checking if
+ * a PTK derived without KDK would result in a matching MIC. */
+ if (!sm->wpa_auth->conf.force_kdk_derivation &&
+ derive_kdk && !no_kdk) {
+ wpa_printf(MSG_DEBUG,
+ "Try new PTK derivation without KDK as a workaround");
+ no_kdk = true;
+ goto try_without_kdk;
+ }
+
if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
wpa_key_mgmt_sae(sm->wpa_key_mgmt))
break;
}
+ if (no_kdk && ok) {
+ /* The workaround worked, so allow the 4-way handshake to be
+ * completed with the PTK that was derived without the KDK. */
+ wpa_printf(MSG_DEBUG,
+ "PTK without KDK worked - misbehaving STA "
+ MACSTR, MAC2STR(sm->addr));
+ }
+
if (!ok && wpa_key_mgmt_wpa_psk_no_sae(sm->wpa_key_mgmt) &&
wpa_auth->conf.radius_psk && wpa_auth->cb->request_radius_psk &&
!sm->waiting_radius_psk) {
@@ -3915,27 +4187,6 @@
#ifdef CONFIG_IEEE80211BE
-void wpa_auth_ml_get_rsn_info(struct wpa_authenticator *a,
- struct wpa_auth_ml_link_rsn_info *info)
-{
- info->rsn_ies = a->wpa_ie;
- info->rsn_ies_len = a->wpa_ie_len;
-
- wpa_printf(MSG_DEBUG, "RSN: MLD: link_id=%u, rsn_ies_len=%zu",
- info->link_id, info->rsn_ies_len);
-}
-
-
-static void wpa_auth_get_ml_rsn_info(struct wpa_authenticator *wpa_auth,
- struct wpa_auth_ml_rsn_info *info)
-{
- if (!wpa_auth->cb->get_ml_rsn_info)
- return;
-
- wpa_auth->cb->get_ml_rsn_info(wpa_auth->cb_ctx, info);
-}
-
-
void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
struct wpa_auth_ml_link_key_info *info,
bool mgmt_frame_prot, bool beacon_prot)
@@ -3998,38 +4249,47 @@
static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm)
{
- struct wpa_authenticator *wpa_auth = sm->wpa_auth;
- struct wpa_group *gsm = sm->group;
- size_t gtk_len = gsm->GTK_len;
- size_t igtk_len;
- size_t kde_len;
- unsigned int n_links;
+ struct wpa_authenticator *wpa_auth;
+ size_t kde_len = 0;
+ int link_id;
if (sm->mld_assoc_link_id < 0)
return 0;
- n_links = sm->n_mld_affiliated_links + 1;
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ if (!sm->mld_links[link_id].valid)
+ continue;
- /* MLO GTK KDE for each link */
- kde_len = n_links * (2 + RSN_SELECTOR_LEN + 1 + 6 + gtk_len);
+ wpa_auth = sm->mld_links[link_id].wpa_auth;
+ if (!wpa_auth || !wpa_auth->group)
+ continue;
- if (!sm->mgmt_frame_prot)
- return kde_len;
+ /* MLO GTK KDE
+ * Header + Key ID + Tx + LinkID + PN + GTK */
+ kde_len += KDE_HDR_LEN + 1 + RSN_PN_LEN;
+ kde_len += wpa_auth->group->GTK_len;
- /* MLO IGTK KDE for each link */
- igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
- kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
+ if (!sm->mgmt_frame_prot)
+ continue;
- if (wpa_auth->conf.tx_bss_auth) {
- wpa_auth = wpa_auth->conf.tx_bss_auth;
- igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+ if (wpa_auth->conf.tx_bss_auth)
+ wpa_auth = wpa_auth->conf.tx_bss_auth;
+
+ /* MLO IGTK KDE
+ * Header + Key ID + IPN + LinkID + IGTK */
+ kde_len += KDE_HDR_LEN + WPA_IGTK_KDE_PREFIX_LEN + 1;
+ kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+
+ if (!wpa_auth->conf.beacon_prot)
+ continue;
+
+ /* MLO BIGTK KDE
+ * Header + Key ID + BIPN + LinkID + BIGTK */
+ kde_len += KDE_HDR_LEN + WPA_BIGTK_KDE_PREFIX_LEN + 1;
+ kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
}
- if (!wpa_auth->conf.beacon_prot)
- return kde_len;
-
- /* MLO BIGTK KDE for each link */
- kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len);
+ wpa_printf(MSG_DEBUG, "MLO Group KDEs len = %zu", kde_len);
return kde_len;
}
@@ -4039,6 +4299,7 @@
{
struct wpa_auth_ml_key_info ml_key_info;
unsigned int i, link_id;
+ u8 *start = pos;
/* First fetch the key information from all the authenticators */
os_memset(&ml_key_info, 0, sizeof(ml_key_info));
@@ -4090,8 +4351,11 @@
i++;
}
- if (!sm->mgmt_frame_prot)
+ if (!sm->mgmt_frame_prot) {
+ wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %ld",
+ pos - start);
return pos;
+ }
/* Add MLO IGTK KDEs */
for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
@@ -4130,8 +4394,11 @@
i++;
}
- if (!sm->wpa_auth->conf.beacon_prot)
+ if (!sm->wpa_auth->conf.beacon_prot) {
+ wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %ld",
+ pos - start);
return pos;
+ }
/* Add MLO BIGTK KDEs */
for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
@@ -4171,6 +4438,7 @@
i++;
}
+ wpa_printf(MSG_DEBUG, "RSN: MLO Group KDE len = %ld", pos - start);
return pos;
}
@@ -4192,12 +4460,33 @@
/* MLO Link KDE for each link */
for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- if (!sm->mld_links[link_id].valid)
+ struct wpa_authenticator *wpa_auth;
+ const u8 *ie, *ieo;
+
+ wpa_auth = wpa_get_link_auth(sm->wpa_auth, link_id);
+ if (!wpa_auth)
continue;
- kde_len += 2 + RSN_SELECTOR_LEN + 1 + ETH_ALEN +
- sm->mld_links[link_id].rsne_len +
- sm->mld_links[link_id].rsnxe_len;
+ kde_len += 2 + RSN_SELECTOR_LEN + 1 + ETH_ALEN;
+ ie = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ WLAN_EID_RSN);
+ ieo = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ sm->rsn_override_2 ?
+ RSNE_OVERRIDE_2_IE_VENDOR_TYPE :
+ RSNE_OVERRIDE_IE_VENDOR_TYPE);
+ if ((sm->rsn_override || sm->rsn_override_2) && ieo)
+ kde_len += 2 + ieo[1 - 4];
+ else
+ kde_len += 2 + ie[1];
+
+ ie = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ WLAN_EID_RSNX);
+ ieo = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+ if ((sm->rsn_override || sm->rsn_override_2) && ieo)
+ kde_len += 2 + ieo[1] - 4;
+ else if (ie)
+ kde_len += 2 + ie[1];
}
kde_len += wpa_auth_ml_group_kdes_len(sm);
@@ -4211,56 +4500,95 @@
{
#ifdef CONFIG_IEEE80211BE
u8 link_id;
+ u8 *start = pos;
if (sm->mld_assoc_link_id < 0)
return pos;
wpa_printf(MSG_DEBUG, "RSN: MLD: Adding MAC Address KDE");
pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR,
- sm->own_mld_addr, ETH_ALEN, NULL, 0);
+ sm->wpa_auth->mld_addr, ETH_ALEN, NULL, 0);
for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- if (!sm->mld_links[link_id].valid)
+ struct wpa_authenticator *wpa_auth;
+ const u8 *rsne, *rsnxe, *rsneo, *rsnxeo;
+ size_t rsne_len, rsnxe_len;
+
+ wpa_auth = wpa_get_link_auth(sm->wpa_auth, link_id);
+ if (!wpa_auth)
continue;
+ rsne = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ WLAN_EID_RSN);
+ rsne_len = rsne ? 2 + rsne[1] : 0;
+ rsneo = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ sm->rsn_override_2 ?
+ RSNE_OVERRIDE_2_IE_VENDOR_TYPE :
+ RSNE_OVERRIDE_IE_VENDOR_TYPE);
+ if ((sm->rsn_override || sm->rsn_override_2) && rsneo)
+ rsne_len = 2 + rsneo[1] - 4;
+ else
+ rsneo = NULL;
+
+ rsnxe = get_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ WLAN_EID_RSNX);
+ rsnxe_len = rsnxe ? 2 + rsnxe[1] : 0;
+ rsnxeo = get_vendor_ie(wpa_auth->wpa_ie, wpa_auth->wpa_ie_len,
+ RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+ if ((sm->rsn_override || sm->rsn_override_2) && rsnxeo)
+ rsnxe_len = 2 + rsnxeo[1] - 4;
+ else
+ rsnxeo = NULL;
+
wpa_printf(MSG_DEBUG,
"RSN: MLO Link: link=%u, len=%zu", link_id,
RSN_SELECTOR_LEN + 1 + ETH_ALEN +
- sm->mld_links[link_id].rsne_len +
- sm->mld_links[link_id].rsnxe_len);
+ rsne_len + rsnxe_len);
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = RSN_SELECTOR_LEN + 1 + ETH_ALEN +
- sm->mld_links[link_id].rsne_len +
- sm->mld_links[link_id].rsnxe_len;
+ rsne_len + rsnxe_len;
RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_MLO_LINK);
pos += RSN_SELECTOR_LEN;
/* Add the Link Information */
*pos = link_id;
- if (sm->mld_links[link_id].rsne_len)
+ if (rsne_len)
*pos |= RSN_MLO_LINK_KDE_LI_RSNE_INFO;
- if (sm->mld_links[link_id].rsnxe_len)
+ if (rsnxe_len)
*pos |= RSN_MLO_LINK_KDE_LI_RSNXE_INFO;
pos++;
- os_memcpy(pos, sm->mld_links[link_id].own_addr, ETH_ALEN);
+ os_memcpy(pos, wpa_auth->addr, ETH_ALEN);
pos += ETH_ALEN;
- if (sm->mld_links[link_id].rsne_len) {
- os_memcpy(pos, sm->mld_links[link_id].rsne,
- sm->mld_links[link_id].rsne_len);
- pos += sm->mld_links[link_id].rsne_len;
+ if (rsne_len) {
+ if (rsneo) {
+ *pos++ = WLAN_EID_RSN;
+ *pos++ = rsneo[1] - 4;
+ os_memcpy(pos, &rsneo[2 + 4], rsneo[1] - 4);
+ pos += rsneo[1] - 4;
+ } else {
+ os_memcpy(pos, rsne, rsne_len);
+ pos += rsne_len;
+ }
}
- if (sm->mld_links[link_id].rsnxe_len) {
- os_memcpy(pos, sm->mld_links[link_id].rsnxe,
- sm->mld_links[link_id].rsnxe_len);
- pos += sm->mld_links[link_id].rsnxe_len;
+ if (rsnxe_len) {
+ if (rsnxeo) {
+ *pos++ = WLAN_EID_RSNX;
+ *pos++ = rsnxeo[1] - 4;
+ os_memcpy(pos, &rsnxeo[2 + 4], rsnxeo[1] - 4);
+ pos += rsnxeo[1] - 4;
+ } else {
+ os_memcpy(pos, rsnxe, rsnxe_len);
+ pos += rsnxe_len;
+ }
}
}
+ wpa_printf(MSG_DEBUG, "RSN: MLO Link KDE len = %ld", pos - start);
pos = wpa_auth_ml_group_kdes(sm, pos);
#endif /* CONFIG_IEEE80211BE */
@@ -4275,7 +4603,7 @@
struct wpa_group *gsm = sm->group;
u8 *wpa_ie;
int secure, gtkidx, encr = 0;
- u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL;
+ u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL, *wpa_ie_buf3 = NULL;
u8 hdr[2];
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
#ifdef CONFIG_IEEE80211BE
@@ -4316,6 +4644,80 @@
wpa_ie = wpa_ie + wpa_ie[1] + 2;
wpa_ie_len = wpa_ie[1] + 2;
}
+ if ((sm->rsn_override &&
+ get_vendor_ie(wpa_ie, wpa_ie_len, RSNE_OVERRIDE_IE_VENDOR_TYPE)) ||
+ (sm->rsn_override_2 &&
+ get_vendor_ie(wpa_ie, wpa_ie_len,
+ RSNE_OVERRIDE_2_IE_VENDOR_TYPE))) {
+ const u8 *mde, *fte, *tie, *tie2 = NULL;
+ const u8 *override_rsne = NULL, *override_rsnxe = NULL;
+ const struct element *elem;
+
+ wpa_printf(MSG_DEBUG,
+ "RSN: Use RSNE/RSNXE override element contents");
+ mde = get_ie(wpa_ie, wpa_ie_len, WLAN_EID_MOBILITY_DOMAIN);
+ fte = get_ie(wpa_ie, wpa_ie_len, WLAN_EID_FAST_BSS_TRANSITION);
+ tie = get_ie(wpa_ie, wpa_ie_len, WLAN_EID_TIMEOUT_INTERVAL);
+ if (tie) {
+ const u8 *next = tie + 2 + tie[1];
+
+ tie2 = get_ie(next, wpa_ie + wpa_ie_len - next,
+ WLAN_EID_TIMEOUT_INTERVAL);
+ }
+ for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC,
+ wpa_ie, wpa_ie_len) {
+ if (elem->datalen >= 4) {
+ if (WPA_GET_BE32(elem->data) ==
+ (sm->rsn_override_2 ?
+ RSNE_OVERRIDE_2_IE_VENDOR_TYPE :
+ RSNE_OVERRIDE_IE_VENDOR_TYPE))
+ override_rsne = &elem->id;
+ if (WPA_GET_BE32(elem->data) ==
+ RSNXE_OVERRIDE_IE_VENDOR_TYPE)
+ override_rsnxe = &elem->id;
+ }
+ }
+ wpa_hexdump(MSG_DEBUG, "EAPOL-Key msg 3/4 IEs before edits",
+ wpa_ie, wpa_ie_len);
+ wpa_ie_buf3 = os_malloc(wpa_ie_len);
+ if (!wpa_ie_buf3)
+ goto done;
+ pos = wpa_ie_buf3;
+ if (override_rsne) {
+ *pos++ = WLAN_EID_RSN;
+ *pos++ = override_rsne[1] - 4;
+ os_memcpy(pos, &override_rsne[2 + 4],
+ override_rsne[1] - 4);
+ pos += override_rsne[1] - 4;
+ }
+ if (mde) {
+ os_memcpy(pos, mde, 2 + mde[1]);
+ pos += 2 + mde[1];
+ }
+ if (fte) {
+ os_memcpy(pos, fte, 2 + fte[1]);
+ pos += 2 + fte[1];
+ }
+ if (tie) {
+ os_memcpy(pos, tie, 2 + tie[1]);
+ pos += 2 + tie[1];
+ }
+ if (tie2) {
+ os_memcpy(pos, tie2, 2 + tie2[1]);
+ pos += 2 + tie2[1];
+ }
+ if (override_rsnxe) {
+ *pos++ = WLAN_EID_RSNX;
+ *pos++ = override_rsnxe[1] - 4;
+ os_memcpy(pos, &override_rsnxe[2 + 4],
+ override_rsnxe[1] - 4);
+ pos += override_rsnxe[1] - 4;
+ }
+ wpa_ie = wpa_ie_buf3;
+ wpa_ie_len = pos - wpa_ie_buf3;
+ wpa_hexdump(MSG_DEBUG, "EAPOL-Key msg 3/4 IEs after edits",
+ wpa_ie, wpa_ie_len);
+ }
#ifdef CONFIG_TESTING_OPTIONS
if (conf->rsne_override_eapol_set) {
wpa_ie_buf2 = replace_ie(
@@ -4434,6 +4836,9 @@
kde_len += wpa_auth_ml_kdes_len(sm);
+ if (sm->ssid_protection)
+ kde_len += 2 + conf->ssid_len;
+
#ifdef CONFIG_TESTING_OPTIONS
if (conf->eapol_m3_elements)
kde_len += wpabuf_len(conf->eapol_m3_elements);
@@ -4553,6 +4958,13 @@
pos = wpa_auth_ml_kdes(sm, pos);
+ if (sm->ssid_protection) {
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = conf->ssid_len;
+ os_memcpy(pos, conf->ssid, conf->ssid_len);
+ pos += conf->ssid_len;
+ }
+
#ifdef CONFIG_TESTING_OPTIONS
if (conf->eapol_m3_elements) {
os_memcpy(pos, wpabuf_head(conf->eapol_m3_elements),
@@ -4575,6 +4987,7 @@
bin_clear_free(kde, kde_len);
os_free(wpa_ie_buf);
os_free(wpa_ie_buf2);
+ os_free(wpa_ie_buf3);
}
@@ -5043,8 +5456,7 @@
#endif /* CONFIG_OCV */
if (sm->GUpdateStationKeys)
- sm->group->GKeyDoneStations--;
- sm->GUpdateStationKeys = false;
+ wpa_gkeydone_sta(sm);
sm->GTimeoutCtr = 0;
/* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
@@ -5058,8 +5470,15 @@
{
SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group);
if (sm->GUpdateStationKeys)
- sm->group->GKeyDoneStations--;
- sm->GUpdateStationKeys = false;
+ wpa_gkeydone_sta(sm);
+ if (sm->wpa_auth->conf.no_disconnect_on_group_keyerror &&
+ sm->wpa == WPA_VERSION_WPA2) {
+ wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "group key handshake failed after %u tries - allow STA to remain connected",
+ sm->wpa_auth->conf.wpa_group_update_count);
+ return;
+ }
sm->Disconnect = true;
sm->disconnect_reason = WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT;
wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
@@ -5353,17 +5772,11 @@
#endif /* CONFIG_WNM_AP */
-static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
- struct wpa_group *group)
+static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
{
int tmp;
- wpa_printf(MSG_DEBUG,
- "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
- group->vlan_id);
- group->changed = true;
- group->wpa_group_state = WPA_GROUP_SETKEYS;
- group->GTKReKey = false;
tmp = group->GM;
group->GM = group->GN;
group->GN = tmp;
@@ -5377,6 +5790,25 @@
* counting the STAs that are marked with GUpdateStationKeys instead of
* including all STAs that could be in not-yet-completed state. */
wpa_gtk_update(wpa_auth, group);
+}
+
+
+static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ wpa_printf(MSG_DEBUG,
+ "WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
+ group->vlan_id);
+ group->changed = true;
+ group->wpa_group_state = WPA_GROUP_SETKEYS;
+ group->GTKReKey = false;
+
+#ifdef CONFIG_IEEE80211BE
+ if (wpa_auth->is_ml)
+ goto skip_update;
+#endif /* CONFIG_IEEE80211BE */
+
+ wpa_group_update_gtk(wpa_auth, group);
if (group->GKeyDoneStations) {
wpa_printf(MSG_DEBUG,
@@ -5384,6 +5816,10 @@
group->GKeyDoneStations);
group->GKeyDoneStations = 0;
}
+
+#ifdef CONFIG_IEEE80211BE
+skip_update:
+#endif /* CONFIG_IEEE80211BE */
wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group);
wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
group->GKeyDoneStations);
@@ -5502,6 +5938,61 @@
}
+static void wpa_clear_changed(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ sm->changed = false;
+ sm->wpa_auth->group->changed = false;
+
+#ifdef CONFIG_IEEE80211BE
+ for_each_sm_auth(sm, link_id)
+ sm->mld_links[link_id].wpa_auth->group->changed = false;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+static void wpa_group_sm_step_links(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!sm || !sm->wpa_auth)
+ return;
+ wpa_group_sm_step(sm->wpa_auth, sm->wpa_auth->group);
+
+#ifdef CONFIG_IEEE80211BE
+ for_each_sm_auth(sm, link_id) {
+ wpa_group_sm_step(sm->mld_links[link_id].wpa_auth,
+ sm->mld_links[link_id].wpa_auth->group);
+ }
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+static bool wpa_group_sm_changed(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_IEEE80211BE
+ int link_id;
+#endif /* CONFIG_IEEE80211BE */
+ bool changed;
+
+ if (!sm || !sm->wpa_auth)
+ return false;
+ changed = sm->wpa_auth->group->changed;
+
+#ifdef CONFIG_IEEE80211BE
+ for_each_sm_auth(sm, link_id)
+ changed |= sm->mld_links[link_id].wpa_auth->group->changed;
+#endif /* CONFIG_IEEE80211BE */
+
+ return changed;
+}
+
+
static int wpa_sm_step(struct wpa_state_machine *sm)
{
if (!sm)
@@ -5520,8 +6011,7 @@
if (sm->pending_deinit)
break;
- sm->changed = false;
- sm->wpa_auth->group->changed = false;
+ wpa_clear_changed(sm);
SM_STEP_RUN(WPA_PTK);
if (sm->pending_deinit)
@@ -5529,8 +6019,8 @@
SM_STEP_RUN(WPA_PTK_GROUP);
if (sm->pending_deinit)
break;
- wpa_group_sm_step(sm->wpa_auth, sm->group);
- } while (sm->changed || sm->wpa_auth->group->changed);
+ wpa_group_sm_step_links(sm);
+ } while (sm->changed || wpa_group_sm_changed(sm));
sm->in_step_loop = 0;
if (sm->pending_deinit) {
@@ -6449,6 +6939,20 @@
}
+void wpa_auth_set_rsn_override(struct wpa_state_machine *sm, bool val)
+{
+ if (sm)
+ sm->rsn_override = val;
+}
+
+
+void wpa_auth_set_rsn_override_2(struct wpa_state_machine *sm, bool val)
+{
+ if (sm)
+ sm->rsn_override_2 = val;
+}
+
+
#ifdef CONFIG_DPP2
void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z)
{
@@ -6460,6 +6964,13 @@
#endif /* CONFIG_DPP2 */
+void wpa_auth_set_ssid_protection(struct wpa_state_machine *sm, bool val)
+{
+ if (sm)
+ sm->ssid_protection = val;
+}
+
+
void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,
u8 val)
{
@@ -6744,8 +7255,10 @@
{
if (!wpa_auth)
return -1;
- eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
- return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL);
+ eloop_cancel_timeout(wpa_rekey_gtk,
+ wpa_get_primary_auth(wpa_auth), NULL);
+ return eloop_register_timeout(0, 0, wpa_rekey_gtk,
+ wpa_get_primary_auth(wpa_auth), NULL);
}
@@ -6833,79 +7346,64 @@
}
-void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+void wpa_auth_set_ml_info(struct wpa_state_machine *sm,
u8 mld_assoc_link_id, struct mld_info *info)
{
#ifdef CONFIG_IEEE80211BE
- struct wpa_auth_ml_rsn_info ml_rsn_info;
- unsigned int link_id, i;
+ unsigned int link_id;
if (!info)
return;
os_memset(sm->mld_links, 0, sizeof(sm->mld_links));
+ sm->n_mld_affiliated_links = 0;
wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
"MLD: Initialization");
- os_memcpy(sm->own_mld_addr, mld_addr, ETH_ALEN);
os_memcpy(sm->peer_mld_addr, info->common_info.mld_addr, ETH_ALEN);
sm->mld_assoc_link_id = mld_assoc_link_id;
- os_memset(&ml_rsn_info, 0, sizeof(ml_rsn_info));
-
- for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
struct mld_link_info *link = &info->links[link_id];
struct mld_link *sm_link = &sm->mld_links[link_id];
+ struct wpa_get_link_auth_ctx ctx;
sm_link->valid = link->valid;
if (!link->valid)
continue;
os_memcpy(sm_link->peer_addr, link->peer_addr, ETH_ALEN);
- os_memcpy(sm_link->own_addr, link->local_addr, ETH_ALEN);
wpa_printf(MSG_DEBUG,
- "WPA_AUTH: MLD: id=%u, addr=" MACSTR " peer=" MACSTR,
+ "WPA_AUTH: MLD: id=%u, peer=" MACSTR,
link_id,
- MAC2STR(sm_link->own_addr),
MAC2STR(sm_link->peer_addr));
- if (link_id != mld_assoc_link_id)
+ if (link_id != mld_assoc_link_id) {
sm->n_mld_affiliated_links++;
-
- ml_rsn_info.links[i++].link_id = link_id;
- }
-
- ml_rsn_info.n_mld_links = i;
-
- wpa_auth_get_ml_rsn_info(sm->wpa_auth, &ml_rsn_info);
-
- for (i = 0; i < ml_rsn_info.n_mld_links; i++) {
- struct mld_link *sm_link;
- const u8 *rsn_ies;
- u8 rsn_ies_len;
-
- sm_link = &sm->mld_links[ml_rsn_info.links[i].link_id];
- rsn_ies = ml_rsn_info.links[i].rsn_ies;
- rsn_ies_len = ml_rsn_info.links[i].rsn_ies_len;
-
- /* This should not really happen */
- if (!rsn_ies || rsn_ies_len < 2 || rsn_ies[0] != WLAN_EID_RSN ||
- rsn_ies[1] + 2 > rsn_ies_len) {
- wpa_printf(MSG_INFO, "WPA_AUTH: MLD: Invalid RSNE");
- continue;
+ ctx.addr = link->local_addr;
+ ctx.mld_addr = NULL;
+ ctx.link_id = -1;
+ ctx.wpa_auth = NULL;
+ wpa_auth_for_each_auth(sm->wpa_auth,
+ wpa_get_link_sta_auth, &ctx);
+ if (ctx.wpa_auth) {
+ sm_link->wpa_auth = ctx.wpa_auth;
+ wpa_group_get(sm_link->wpa_auth,
+ sm_link->wpa_auth->group);
+ }
+ } else {
+ sm_link->wpa_auth = sm->wpa_auth;
}
- sm_link->rsne = rsn_ies;
- sm_link->rsne_len = rsn_ies[1] + 2;
-
- if (rsn_ies[1] + 2UL + 2UL < rsn_ies_len &&
- rsn_ies[rsn_ies[1] + 2] == WLAN_EID_RSNX) {
- sm_link->rsnxe = rsn_ies + 2 + rsn_ies[1];
- sm_link->rsnxe_len = sm_link->rsnxe[1] + 2;
- }
+ if (!sm_link->wpa_auth)
+ wpa_printf(MSG_ERROR,
+ "Unable to find authenticator object for ML STA "
+ MACSTR " on link id %d",
+ MAC2STR(sm->wpa_auth->mld_addr),
+ link_id);
}
#endif /* CONFIG_IEEE80211BE */
}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 4f6bb76..86cb4e8 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -173,6 +173,8 @@
int wpa;
int extended_key_id;
int wpa_key_mgmt;
+ int rsn_override_key_mgmt;
+ int rsn_override_key_mgmt_2;
int wpa_pairwise;
int wpa_group;
int wpa_group_rekey;
@@ -184,6 +186,8 @@
u32 wpa_pairwise_update_count;
int wpa_disable_eapol_key_retries;
int rsn_pairwise;
+ int rsn_override_pairwise;
+ int rsn_override_pairwise_2;
int rsn_preauth;
int eapol_version;
int wmm_enabled;
@@ -192,15 +196,17 @@
int okc;
int tx_status;
enum mfp_options ieee80211w;
+ enum mfp_options rsn_override_mfp;
+ enum mfp_options rsn_override_mfp_2;
int beacon_prot;
int group_mgmt_cipher;
int sae_require_mfp;
#ifdef CONFIG_OCV
int ocv; /* Operating Channel Validation */
#endif /* CONFIG_OCV */
-#ifdef CONFIG_IEEE80211R_AP
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
+#ifdef CONFIG_IEEE80211R_AP
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
u8 r0_key_holder[FT_R0KH_ID_MAX_LEN];
size_t r0_key_holder_len;
@@ -283,10 +289,20 @@
bool radius_psk;
+ bool no_disconnect_on_group_keyerror;
+
/* Pointer to Multi-BSSID transmitted BSS authenticator instance.
* Set only in nontransmitted BSSs, i.e., is NULL for transmitted BSS
* and in BSSs that are not part of a Multi-BSSID set. */
struct wpa_authenticator *tx_bss_auth;
+
+#ifdef CONFIG_IEEE80211BE
+ const u8 *mld_addr;
+ int link_id;
+ struct wpa_authenticator *first_link_auth;
+#endif /* CONFIG_IEEE80211BE */
+
+ bool ssid_protection;
};
typedef enum {
@@ -299,16 +315,6 @@
WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx
} wpa_eapol_variable;
-struct wpa_auth_ml_rsn_info {
- unsigned int n_mld_links;
-
- struct wpa_auth_ml_link_rsn_info {
- unsigned int link_id;
- const u8 *rsn_ies;
- size_t rsn_ies_len;
- } links[MAX_NUM_MLD_LINKS];
-};
-
struct wpa_auth_ml_key_info {
unsigned int n_mld_links;
bool mgmt_frame_prot;
@@ -402,7 +408,6 @@
size_t ltf_keyseed_len);
#endif /* CONFIG_PASN */
#ifdef CONFIG_IEEE80211BE
- int (*get_ml_rsn_info)(void *ctx, struct wpa_auth_ml_rsn_info *info);
int (*get_ml_key_info)(void *ctx, struct wpa_auth_ml_key_info *info);
#endif /* CONFIG_IEEE80211BE */
int (*get_drv_flags)(void *ctx, u64 *drv_flags, u64 *drv_flags2);
@@ -431,7 +436,8 @@
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *rsnxe, size_t rsnxe_len,
const u8 *mdie, size_t mdie_len,
- const u8 *owe_dh, size_t owe_dh_len);
+ const u8 *owe_dh, size_t owe_dh_len,
+ struct wpa_state_machine *assoc_sm);
int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *osen_ie, size_t osen_ie_len);
@@ -522,9 +528,9 @@
size_t max_len, int auth_alg,
const u8 *req_ies, size_t req_ies_len,
int omit_rsnxe);
-void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
+void wpa_ft_process_auth(struct wpa_state_machine *sm,
u16 auth_transaction, const u8 *ies, size_t ies_len,
- void (*cb)(void *ctx, const u8 *dst, const u8 *bssid,
+ void (*cb)(void *ctx, const u8 *dst,
u16 auth_transaction, u16 resp,
const u8 *ies, size_t ies_len),
void *ctx);
@@ -607,7 +613,10 @@
bool wpa_auth_write_fd_rsn_info(struct wpa_authenticator *wpa_auth,
u8 *fd_rsn_info);
void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg);
+void wpa_auth_set_rsn_override(struct wpa_state_machine *sm, bool val);
+void wpa_auth_set_rsn_override_2(struct wpa_state_machine *sm, bool val);
void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z);
+void wpa_auth_set_ssid_protection(struct wpa_state_machine *sm, bool val);
void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,
u8 val);
@@ -645,12 +654,19 @@
void wpa_auth_sta_radius_psk_resp(struct wpa_state_machine *sm, bool success);
-void wpa_auth_set_ml_info(struct wpa_state_machine *sm, const u8 *mld_addr,
+void wpa_auth_set_ml_info(struct wpa_state_machine *sm,
u8 mld_assoc_link_id, struct mld_info *info);
-void wpa_auth_ml_get_rsn_info(struct wpa_authenticator *a,
- struct wpa_auth_ml_link_rsn_info *info);
void wpa_auth_ml_get_key_info(struct wpa_authenticator *a,
struct wpa_auth_ml_link_key_info *info,
bool mgmt_frame_prot, bool beacon_prot);
+void wpa_release_link_auth_ref(struct wpa_state_machine *sm,
+ int release_link_id);
+
+#define for_each_sm_auth(sm, link_id) \
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) \
+ if (sm->mld_links[link_id].valid && \
+ sm->mld_links[link_id].wpa_auth && \
+ sm->wpa_auth != sm->mld_links[link_id].wpa_auth)
+
#endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 7744ed6..de16c31 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -3442,9 +3442,9 @@
}
-void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
+void wpa_ft_process_auth(struct wpa_state_machine *sm,
u16 auth_transaction, const u8 *ies, size_t ies_len,
- void (*cb)(void *ctx, const u8 *dst, const u8 *bssid,
+ void (*cb)(void *ctx, const u8 *dst,
u16 auth_transaction, u16 status,
const u8 *ies, size_t ies_len),
void *ctx)
@@ -3462,7 +3462,8 @@
wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR
" BSSID=" MACSTR " transaction=%d",
- MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction);
+ MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr),
+ auth_transaction);
sm->ft_pending_cb = cb;
sm->ft_pending_cb_ctx = ctx;
sm->ft_pending_auth_transaction = auth_transaction;
@@ -3480,8 +3481,7 @@
MAC2STR(sm->addr), auth_transaction + 1, status,
status2str(status));
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
- cb(ctx, sm->addr, bssid, auth_transaction + 1, status,
- resp_ies, resp_ies_len);
+ cb(ctx, sm->addr, auth_transaction + 1, status, resp_ies, resp_ies_len);
os_free(resp_ies);
}
@@ -3810,7 +3810,7 @@
}
-static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst, const u8 *bssid,
+static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst,
u16 auth_transaction, u16 resp,
const u8 *ies, size_t ies_len)
{
@@ -4339,7 +4339,7 @@
wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
" - status %u", MAC2STR(sm->addr), status);
- sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr, sm->wpa_auth->addr,
+ sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr,
sm->ft_pending_auth_transaction + 1, status,
resp_ies, resp_ies_len);
os_free(resp_ies);
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 34de45c..e88644f 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -45,6 +45,8 @@
wconf->wpa = conf->wpa;
wconf->extended_key_id = conf->extended_key_id;
wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
+ wconf->rsn_override_key_mgmt = conf->rsn_override_key_mgmt;
+ wconf->rsn_override_key_mgmt_2 = conf->rsn_override_key_mgmt_2;
wconf->wpa_pairwise = conf->wpa_pairwise;
wconf->wpa_group = conf->wpa_group;
wconf->wpa_group_rekey = conf->wpa_group_rekey;
@@ -56,6 +58,8 @@
conf->wpa_disable_eapol_key_retries;
wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count;
wconf->rsn_pairwise = conf->rsn_pairwise;
+ wconf->rsn_override_pairwise = conf->rsn_override_pairwise;
+ wconf->rsn_override_pairwise_2 = conf->rsn_override_pairwise_2;
wconf->rsn_preauth = conf->rsn_preauth;
wconf->eapol_version = conf->eapol_version;
#ifdef CONFIG_MACSEC
@@ -70,14 +74,17 @@
#endif /* CONFIG_OCV */
wconf->okc = conf->okc;
wconf->ieee80211w = conf->ieee80211w;
+ wconf->rsn_override_mfp = conf->rsn_override_mfp;
+ wconf->rsn_override_mfp_2 = conf->rsn_override_mfp_2;
wconf->beacon_prot = conf->beacon_prot;
wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
wconf->sae_require_mfp = conf->sae_require_mfp;
-#ifdef CONFIG_IEEE80211R_AP
+ wconf->ssid_protection = conf->ssid_protection;
wconf->ssid_len = conf->ssid.ssid_len;
if (wconf->ssid_len > SSID_MAX_LEN)
wconf->ssid_len = SSID_MAX_LEN;
os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len);
+#ifdef CONFIG_IEEE80211R_AP
os_memcpy(wconf->mobility_domain, conf->mobility_domain,
MOBILITY_DOMAIN_ID_LEN);
if (conf->nas_identifier &&
@@ -227,6 +234,9 @@
#endif /* CONFIG_PASN */
wconf->radius_psk = conf->wpa_psk_radius == PSK_RADIUS_DURING_4WAY_HS;
+ wconf->no_disconnect_on_group_keyerror =
+ conf->bss_max_idle && conf->ap_max_inactivity &&
+ conf->no_disconnect_on_group_keyerror;
}
@@ -1536,53 +1546,11 @@
#ifdef CONFIG_IEEE80211BE
-static int hostapd_wpa_auth_get_ml_rsn_info(void *ctx,
- struct wpa_auth_ml_rsn_info *info)
-{
- struct hostapd_data *hapd = ctx;
- unsigned int i, j;
-
- wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get RSN info CB: n_mld_links=%u",
- info->n_mld_links);
-
- if (!hapd->conf->mld_ap || !hapd->iface || !hapd->iface->interfaces)
- return -1;
-
- for (i = 0; i < info->n_mld_links; i++) {
- unsigned int link_id = info->links[i].link_id;
-
- wpa_printf(MSG_DEBUG,
- "WPA_AUTH: MLD: Get link RSN CB: link_id=%u",
- link_id);
-
- for (j = 0; j < hapd->iface->interfaces->count; j++) {
- struct hostapd_iface *iface =
- hapd->iface->interfaces->iface[j];
-
- if (!hostapd_is_ml_partner(hapd, iface->bss[0]) ||
- link_id != iface->bss[0]->mld_link_id ||
- !iface->bss[0]->wpa_auth)
- continue;
-
- wpa_auth_ml_get_rsn_info(iface->bss[0]->wpa_auth,
- &info->links[i]);
- break;
- }
-
- if (j == hapd->iface->interfaces->count)
- wpa_printf(MSG_DEBUG,
- "WPA_AUTH: MLD: link=%u not found", link_id);
- }
-
- return 0;
-}
-
-
static int hostapd_wpa_auth_get_ml_key_info(void *ctx,
struct wpa_auth_ml_key_info *info)
{
struct hostapd_data *hapd = ctx;
- unsigned int i, j;
+ unsigned int i;
wpa_printf(MSG_DEBUG, "WPA_AUTH: MLD: Get key info CB: n_mld_links=%u",
info->n_mld_links);
@@ -1591,29 +1559,35 @@
return -1;
for (i = 0; i < info->n_mld_links; i++) {
+ struct hostapd_data *bss;
u8 link_id = info->links[i].link_id;
+ bool link_bss_found = false;
wpa_printf(MSG_DEBUG,
"WPA_AUTH: MLD: Get link info CB: link_id=%u",
link_id);
- for (j = 0; j < hapd->iface->interfaces->count; j++) {
- struct hostapd_iface *iface =
- hapd->iface->interfaces->iface[j];
-
- if (!hostapd_is_ml_partner(hapd, iface->bss[0]) ||
- link_id != iface->bss[0]->mld_link_id ||
- !iface->bss[0]->wpa_auth)
- continue;
-
- wpa_auth_ml_get_key_info(iface->bss[0]->wpa_auth,
+ if (hapd->mld_link_id == link_id) {
+ wpa_auth_ml_get_key_info(hapd->wpa_auth,
&info->links[i],
info->mgmt_frame_prot,
info->beacon_prot);
+ continue;
+ }
+
+ for_each_mld_link(bss, hapd) {
+ if (bss == hapd || bss->mld_link_id != link_id)
+ continue;
+
+ wpa_auth_ml_get_key_info(bss->wpa_auth,
+ &info->links[i],
+ info->mgmt_frame_prot,
+ info->beacon_prot);
+ link_bss_found = true;
break;
}
- if (j == hapd->iface->interfaces->count)
+ if (!link_bss_found)
wpa_printf(MSG_DEBUG,
"WPA_AUTH: MLD: link=%u not found", link_id);
}
@@ -1689,7 +1663,6 @@
.set_ltf_keyseed = hostapd_set_ltf_keyseed,
#endif /* CONFIG_PASN */
#ifdef CONFIG_IEEE80211BE
- .get_ml_rsn_info = hostapd_wpa_auth_get_ml_rsn_info,
.get_ml_key_info = hostapd_wpa_auth_get_ml_key_info,
#endif /* CONFIG_IEEE80211BE */
.get_drv_flags = hostapd_wpa_auth_get_drv_flags,
@@ -1740,6 +1713,27 @@
!!(hapd->iface->drv_flags2 &
WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP);
+#ifdef CONFIG_IEEE80211BE
+ _conf.mld_addr = NULL;
+ _conf.link_id = -1;
+ _conf.first_link_auth = NULL;
+
+ if (hapd->conf->mld_ap) {
+ struct hostapd_data *lhapd;
+
+ _conf.mld_addr = hapd->mld->mld_addr;
+ _conf.link_id = hapd->mld_link_id;
+
+ for_each_mld_link(lhapd, hapd) {
+ if (lhapd == hapd)
+ continue;
+
+ if (lhapd->wpa_auth)
+ _conf.first_link_auth = lhapd->wpa_auth;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
if (hapd->wpa_auth == NULL) {
wpa_printf(MSG_ERROR, "WPA initialization failed.");
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 9ba8304..29988c2 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -124,6 +124,9 @@
u32 dot11RSNAStatsTKIPLocalMICFailures;
u32 dot11RSNAStatsTKIPRemoteMICFailures;
+ bool rsn_override;
+ bool rsn_override_2;
+
#ifdef CONFIG_IEEE80211R_AP
u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
* first 384 bits of MSK */
@@ -136,7 +139,7 @@
size_t r0kh_id_len;
u8 *assoc_resp_ftie;
- void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid,
+ void (*ft_pending_cb)(void *ctx, const u8 *dst,
u16 auth_transaction, u16 status,
const u8 *ies, size_t ies_len);
void *ft_pending_cb_ctx;
@@ -172,7 +175,6 @@
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_IEEE80211BE
- u8 own_mld_addr[ETH_ALEN];
u8 peer_mld_addr[ETH_ALEN];
s8 mld_assoc_link_id;
u8 n_mld_affiliated_links;
@@ -180,14 +182,12 @@
struct mld_link {
bool valid;
u8 peer_addr[ETH_ALEN];
- u8 own_addr[ETH_ALEN];
- const u8 *rsne;
- size_t rsne_len;
- const u8 *rsnxe;
- size_t rsnxe_len;
+ struct wpa_authenticator *wpa_auth;
} mld_links[MAX_NUM_MLD_LINKS];
#endif /* CONFIG_IEEE80211BE */
+
+ bool ssid_protection;
};
@@ -251,6 +251,9 @@
u8 *wpa_ie;
size_t wpa_ie_len;
+ u8 *rsne_override; /* RSNE with overridden payload */
+ u8 *rsne_override_2; /* RSNE with overridden (2) payload */
+ u8 *rsnxe_override; /* RSNXE with overridden payload */
u8 addr[ETH_ALEN];
@@ -262,6 +265,13 @@
#ifdef CONFIG_P2P
struct bitfield *ip_pool;
#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_IEEE80211BE
+ bool is_ml;
+ u8 mld_addr[ETH_ALEN];
+ u8 link_id;
+ bool primary_auth;
+#endif /* CONFIG_IEEE80211BE */
};
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index a5f2861..f4f9cc8 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -89,7 +89,8 @@
}
-static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf)
+static u16 wpa_own_rsn_capab(struct wpa_auth_config *conf,
+ enum mfp_options mfp)
{
u16 capab = 0;
@@ -99,9 +100,9 @@
/* 4 PTKSA replay counters when using WMM */
capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
}
- if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ if (mfp != NO_MGMT_FRAME_PROTECTION) {
capab |= WPA_CAPABILITY_MFPC;
- if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+ if (mfp == MGMT_FRAME_PROTECTION_REQUIRED)
capab |= WPA_CAPABILITY_MFPR;
}
#ifdef CONFIG_OCV
@@ -119,24 +120,19 @@
}
-int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
- const u8 *pmkid)
+static u8 * rsne_write_data(u8 *buf, size_t len, u8 *pos, int group,
+ int pairwise, int key_mgmt, u16 rsn_capab,
+ const u8 *pmkid, enum mfp_options mfp,
+ int group_mgmt_cipher)
{
- struct rsn_ie_hdr *hdr;
int num_suites, res;
- u8 *pos, *count;
+ u8 *count;
u32 suite;
- hdr = (struct rsn_ie_hdr *) buf;
- hdr->elem_id = WLAN_EID_RSN;
- WPA_PUT_LE16(hdr->version, RSN_VERSION);
- pos = (u8 *) (hdr + 1);
-
- suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group);
+ suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group);
if (suite == 0) {
- wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
- conf->wpa_group);
- return -1;
+ wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", group);
+ return NULL;
}
RSN_SELECTOR_PUT(pos, suite);
pos += RSN_SELECTOR_LEN;
@@ -153,7 +149,7 @@
}
#endif /* CONFIG_RSN_TESTING */
- res = rsn_cipher_put_suites(pos, conf->rsn_pairwise);
+ res = rsn_cipher_put_suites(pos, pairwise);
num_suites += res;
pos += res * RSN_SELECTOR_LEN;
@@ -167,8 +163,8 @@
if (num_suites == 0) {
wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
- conf->rsn_pairwise);
- return -1;
+ pairwise);
+ return NULL;
}
WPA_PUT_LE16(count, num_suites);
@@ -184,102 +180,102 @@
}
#endif /* CONFIG_RSN_TESTING */
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+ if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+ if (key_mgmt & WPA_KEY_MGMT_PSK) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#ifdef CONFIG_IEEE80211R_AP
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+ if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#ifdef CONFIG_SHA384
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+ if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#endif /* CONFIG_SHA384 */
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+ if (key_mgmt & WPA_KEY_MGMT_FT_PSK) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SHA384
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) {
+ if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA384);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#endif /* CONFIG_SHA384 */
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+ if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+ if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#ifdef CONFIG_SAE
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
+ if (key_mgmt & WPA_KEY_MGMT_SAE) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+ if (key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+ if (key_mgmt & WPA_KEY_MGMT_FT_SAE) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+ if (key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#endif /* CONFIG_SAE */
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+ if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+ if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#ifdef CONFIG_FILS
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+ if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+ if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#ifdef CONFIG_IEEE80211R_AP
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+ if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+ if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
pos += RSN_SELECTOR_LEN;
num_suites++;
@@ -287,28 +283,28 @@
#endif /* CONFIG_IEEE80211R_AP */
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) {
+ if (key_mgmt & WPA_KEY_MGMT_OWE) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+ if (key_mgmt & WPA_KEY_MGMT_DPP) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#endif /* CONFIG_DPP */
#ifdef CONFIG_HS20
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OSEN) {
+ if (key_mgmt & WPA_KEY_MGMT_OSEN) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
#endif /* CONFIG_HS20 */
#ifdef CONFIG_PASN
- if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) {
+ if (key_mgmt & WPA_KEY_MGMT_PASN) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN);
pos += RSN_SELECTOR_LEN;
num_suites++;
@@ -325,18 +321,18 @@
if (num_suites == 0) {
wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
- conf->wpa_key_mgmt);
- return -1;
+ key_mgmt);
+ return NULL;
}
WPA_PUT_LE16(count, num_suites);
/* RSN Capabilities */
- WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf));
+ WPA_PUT_LE16(pos, rsn_capab);
pos += 2;
if (pmkid) {
if (2 + PMKID_LEN > buf + len - pos)
- return -1;
+ return NULL;
/* PMKID Count */
WPA_PUT_LE16(pos, 1);
pos += 2;
@@ -344,18 +340,19 @@
pos += PMKID_LEN;
}
- if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
- conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) {
+
+ if (mfp != NO_MGMT_FRAME_PROTECTION &&
+ group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) {
if (2 + 4 > buf + len - pos)
- return -1;
- if (pmkid == NULL) {
+ return NULL;
+ if (!pmkid) {
/* PMKID Count */
WPA_PUT_LE16(pos, 0);
pos += 2;
}
/* Management Group Cipher Suite */
- switch (conf->group_mgmt_cipher) {
+ switch (group_mgmt_cipher) {
case WPA_CIPHER_AES_128_CMAC:
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
break;
@@ -371,8 +368,8 @@
default:
wpa_printf(MSG_DEBUG,
"Invalid group management cipher (0x%x)",
- conf->group_mgmt_cipher);
- return -1;
+ group_mgmt_cipher);
+ return NULL;
}
pos += RSN_SELECTOR_LEN;
}
@@ -384,12 +381,12 @@
* the element.
*/
int pmkid_count_set = pmkid != NULL;
- if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION)
+ if (mfp != NO_MGMT_FRAME_PROTECTION)
pmkid_count_set = 1;
/* PMKID Count */
WPA_PUT_LE16(pos, 0);
pos += 2;
- if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+ if (mfp == NO_MGMT_FRAME_PROTECTION) {
/* Management Group Cipher Suite */
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
pos += RSN_SELECTOR_LEN;
@@ -399,6 +396,27 @@
pos += 17;
}
#endif /* CONFIG_RSN_TESTING */
+ return pos;
+}
+
+
+int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
+ const u8 *pmkid)
+{
+ struct rsn_ie_hdr *hdr;
+ u8 *pos;
+
+ hdr = (struct rsn_ie_hdr *) buf;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ pos = rsne_write_data(buf, len, pos, conf->wpa_group,
+ conf->rsn_pairwise, conf->wpa_key_mgmt,
+ wpa_own_rsn_capab(conf, conf->ieee80211w), pmkid,
+ conf->ieee80211w, conf->group_mgmt_cipher);
+ if (!pos)
+ return -1;
hdr->len = (pos - buf) - 2;
@@ -406,16 +424,74 @@
}
-int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
+static int wpa_write_rsne_override(struct wpa_auth_config *conf, u8 *buf,
+ size_t len)
{
- u8 *pos = buf;
- u16 capab = 0;
- size_t flen;
+ u8 *pos, *len_pos;
- if (wpa_key_mgmt_sae(conf->wpa_key_mgmt) &&
+ pos = buf;
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ len_pos = pos++;
+
+ WPA_PUT_BE32(pos, RSNE_OVERRIDE_IE_VENDOR_TYPE);
+ pos += 4;
+
+ WPA_PUT_LE16(pos, RSN_VERSION);
+ pos += 2;
+
+ pos = rsne_write_data(buf, len, pos, conf->wpa_group,
+ conf->rsn_override_pairwise,
+ conf->rsn_override_key_mgmt,
+ wpa_own_rsn_capab(conf, conf->rsn_override_mfp),
+ NULL, conf->rsn_override_mfp,
+ conf->group_mgmt_cipher);
+ if (!pos)
+ return -1;
+
+ *len_pos = (pos - buf) - 2;
+
+ return pos - buf;
+}
+
+
+static int wpa_write_rsne_override_2(struct wpa_auth_config *conf, u8 *buf,
+ size_t len)
+{
+ u8 *pos, *len_pos;
+
+ pos = buf;
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ len_pos = pos++;
+
+ WPA_PUT_BE32(pos, RSNE_OVERRIDE_2_IE_VENDOR_TYPE);
+ pos += 4;
+
+ WPA_PUT_LE16(pos, RSN_VERSION);
+ pos += 2;
+
+ pos = rsne_write_data(buf, len, pos, conf->wpa_group,
+ conf->rsn_override_pairwise_2,
+ conf->rsn_override_key_mgmt_2,
+ wpa_own_rsn_capab(conf, conf->rsn_override_mfp_2),
+ NULL, conf->rsn_override_mfp_2,
+ conf->group_mgmt_cipher);
+ if (!pos)
+ return -1;
+
+ *len_pos = (pos - buf) - 2;
+
+ return pos - buf;
+}
+
+
+static u32 rsnxe_capab(struct wpa_auth_config *conf, int key_mgmt)
+{
+ u32 capab = 0;
+
+ if (wpa_key_mgmt_sae(key_mgmt) &&
(conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
conf->sae_pwe == SAE_PWE_BOTH || conf->sae_pk ||
- wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt))) {
+ wpa_key_mgmt_sae_ext_key(key_mgmt))) {
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
if (conf->sae_pk)
@@ -429,6 +505,53 @@
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
if (conf->prot_range_neg)
capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+ if (conf->ssid_protection)
+ capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
+
+ return capab;
+}
+
+
+int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
+{
+ u8 *pos = buf;
+ u32 capab = 0, tmp;
+ size_t flen;
+
+ capab = rsnxe_capab(conf, conf->wpa_key_mgmt);
+
+ if (!capab)
+ return 0; /* no supported extended RSN capabilities */
+ tmp = capab;
+ flen = 0;
+ while (tmp) {
+ flen++;
+ tmp >>= 8;
+ }
+ if (len < 2 + flen)
+ return -1;
+ capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
+
+ *pos++ = WLAN_EID_RSNX;
+ *pos++ = flen;
+ while (capab) {
+ *pos++ = capab & 0xff;
+ capab >>= 8;
+ }
+
+ return pos - buf;
+}
+
+
+static int wpa_write_rsnxe_override(struct wpa_auth_config *conf, u8 *buf,
+ size_t len)
+{
+ u8 *pos = buf;
+ u16 capab;
+ size_t flen;
+
+ capab = rsnxe_capab(conf, conf->rsn_override_key_mgmt |
+ conf->rsn_override_key_mgmt_2);
flen = (capab & 0xff00) ? 2 : 1;
if (!capab)
@@ -437,8 +560,11 @@
return -1;
capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
- *pos++ = WLAN_EID_RSNX;
- *pos++ = flen;
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = 4 + flen;
+ WPA_PUT_BE32(pos, RSNXE_OVERRIDE_IE_VENDOR_TYPE);
+ pos += 4;
+
*pos++ = capab & 0x00ff;
capab >>= 8;
if (capab)
@@ -501,7 +627,7 @@
int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
{
- u8 *pos, buf[128];
+ u8 *pos, buf[256];
int res;
#ifdef CONFIG_TESTING_OPTIONS
@@ -554,6 +680,31 @@
return res;
pos += res;
}
+ if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ wpa_auth->conf.rsn_override_key_mgmt) {
+ res = wpa_write_rsne_override(&wpa_auth->conf,
+ pos, buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
+ if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ wpa_auth->conf.rsn_override_key_mgmt_2) {
+ res = wpa_write_rsne_override_2(&wpa_auth->conf, pos,
+ buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
+ if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ (wpa_auth->conf.rsn_override_key_mgmt ||
+ wpa_auth->conf.rsn_override_key_mgmt_2)) {
+ res = wpa_write_rsnxe_override(&wpa_auth->conf, pos,
+ buf + sizeof(buf) - pos);
+ if (res < 0)
+ return res;
+ pos += res;
+ }
os_free(wpa_auth->wpa_ie);
wpa_auth->wpa_ie = os_malloc(pos - buf);
@@ -562,6 +713,59 @@
os_memcpy(wpa_auth->wpa_ie, buf, pos - buf);
wpa_auth->wpa_ie_len = pos - buf;
+ if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ wpa_auth->conf.rsn_override_key_mgmt) {
+ res = wpa_write_rsne_override(&wpa_auth->conf, buf,
+ sizeof(buf));
+ if (res < 0)
+ return res;
+ os_free(wpa_auth->rsne_override);
+ wpa_auth->rsne_override = os_malloc(res - 4);
+ if (!wpa_auth->rsne_override)
+ return -1;
+ pos = wpa_auth->rsne_override;
+ *pos++ = WLAN_EID_RSN;
+ *pos++ = res - 2 - 4;
+ os_memcpy(pos, &buf[2 + 4], res - 2 - 4);
+ }
+
+ if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ wpa_auth->conf.rsn_override_key_mgmt_2) {
+ res = wpa_write_rsne_override_2(&wpa_auth->conf, buf,
+ sizeof(buf));
+ if (res < 0)
+ return res;
+ os_free(wpa_auth->rsne_override_2);
+ wpa_auth->rsne_override_2 = os_malloc(res - 4);
+ if (!wpa_auth->rsne_override_2)
+ return -1;
+ pos = wpa_auth->rsne_override_2;
+ *pos++ = WLAN_EID_RSN;
+ *pos++ = res - 2 - 4;
+ os_memcpy(pos, &buf[2 + 4], res - 2 - 4);
+ }
+
+ if ((wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
+ (wpa_auth->conf.rsn_override_key_mgmt ||
+ wpa_auth->conf.rsn_override_key_mgmt_2)) {
+ res = wpa_write_rsnxe_override(&wpa_auth->conf, buf,
+ sizeof(buf));
+ if (res < 0)
+ return res;
+ os_free(wpa_auth->rsnxe_override);
+ if (res == 0) {
+ wpa_auth->rsnxe_override = NULL;
+ return 0;
+ }
+ wpa_auth->rsnxe_override = os_malloc(res - 4);
+ if (!wpa_auth->rsnxe_override)
+ return -1;
+ pos = wpa_auth->rsnxe_override;
+ *pos++ = WLAN_EID_RSNX;
+ *pos++ = res - 2 - 4;
+ os_memcpy(pos, &buf[2 + 4], res - 2 - 4);
+ }
+
return 0;
}
@@ -608,7 +812,8 @@
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *rsnxe, size_t rsnxe_len,
const u8 *mdie, size_t mdie_len,
- const u8 *owe_dh, size_t owe_dh_len)
+ const u8 *owe_dh, size_t owe_dh_len,
+ struct wpa_state_machine *assoc_sm)
{
struct wpa_auth_config *conf = &wpa_auth->conf;
struct wpa_ie_data data;
@@ -765,7 +970,9 @@
return WPA_INVALID_GROUP;
}
- key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt;
+ key_mgmt = data.key_mgmt & (wpa_auth->conf.wpa_key_mgmt |
+ wpa_auth->conf.rsn_override_key_mgmt |
+ wpa_auth->conf.rsn_override_key_mgmt_2);
if (!key_mgmt) {
wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from "
MACSTR, data.key_mgmt, MAC2STR(sm->addr));
@@ -835,7 +1042,10 @@
sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
if (version == WPA_PROTO_RSN)
- ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise;
+ ciphers = data.pairwise_cipher &
+ (wpa_auth->conf.rsn_pairwise |
+ wpa_auth->conf.rsn_override_pairwise |
+ wpa_auth->conf.rsn_override_pairwise_2);
else
ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise;
if (!ciphers) {
@@ -956,6 +1166,15 @@
else
sm->wpa = WPA_VERSION_WPA;
+ if (assoc_sm) {
+ /* For ML association link STA cannot choose a different
+ * AKM or pairwise cipher from association STA */
+ if (sm->wpa_key_mgmt != assoc_sm->wpa_key_mgmt)
+ return WPA_INVALID_AKMP;
+ if (sm->pairwise != assoc_sm->pairwise)
+ return WPA_INVALID_PAIRWISE;
+ }
+
#if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS)
if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 ||
sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) &&
@@ -1212,7 +1431,7 @@
return false;
/* RSN Capability (B0..B15) */
- WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf));
+ WPA_PUT_LE16(pos, wpa_own_rsn_capab(conf, conf->ieee80211w));
pos += 2;
/* Group Data Cipher Suite Selector (B16..B21) */
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 4de88d1..10f9c4a 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -140,6 +140,14 @@
elems->sae_pk = pos + 4;
elems->sae_pk_len = elen - 4;
break;
+ case WFA_RSNE_OVERRIDE_OUI_TYPE:
+ elems->rsne_override = pos;
+ elems->rsne_override_len = elen;
+ break;
+ case WFA_RSNE_OVERRIDE_2_OUI_TYPE:
+ elems->rsne_override_2 = pos;
+ elems->rsne_override_2_len = elen;
+ break;
default:
wpa_printf(MSG_MSGDUMP, "Unknown WFA "
"information element ignored "
@@ -615,6 +623,12 @@
elems->rrm_enabled = pos;
elems->rrm_enabled_len = elen;
break;
+ case WLAN_EID_MULTIPLE_BSSID:
+ if (elen < 1)
+ break;
+ elems->mbssid = pos;
+ elems->mbssid_len = elen;
+ break;
case WLAN_EID_CAG_NUMBER:
elems->cag_number = pos;
elems->cag_number_len = elen;
@@ -1497,8 +1511,6 @@
*op_class = 126;
else if (sec_channel == -1)
*op_class = 127;
- else if (freq <= 5805)
- *op_class = 124;
else
*op_class = 125;
@@ -3130,8 +3142,12 @@
bool ieee802_11_rsnx_capab(const u8 *rsnxe, unsigned int capab)
{
- return ieee802_11_rsnx_capab_len(rsnxe ? rsnxe + 2 : NULL,
- rsnxe ? rsnxe[1] : 0, capab);
+ if (!rsnxe)
+ return false;
+ if (rsnxe[0] == WLAN_EID_VENDOR_SPECIFIC && rsnxe[1] >= 4 + 1)
+ return ieee802_11_rsnx_capab_len(rsnxe + 2 + 4, rsnxe[1] - 4,
+ capab);
+ return ieee802_11_rsnx_capab_len(rsnxe + 2, rsnxe[1], capab);
}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 56eb0df..17e0822 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -115,6 +115,9 @@
const u8 *tdls_mle;
const u8 *prior_access_mle;
const u8 *mbssid_known_bss;
+ const u8 *mbssid;
+ const u8 *rsne_override;
+ const u8 *rsne_override_2;
u8 ssid_len;
u8 supp_rates_len;
@@ -177,6 +180,9 @@
size_t tdls_mle_len;
size_t prior_access_mle_len;
u8 mbssid_known_bss_len;
+ u8 mbssid_len;
+ size_t rsne_override_len;
+ size_t rsne_override_2_len;
struct mb_ies_info mb_ies;
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 644bebd..4cc6e41 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -616,6 +616,7 @@
#define WLAN_RSNX_CAPAB_SECURE_RTT 9
#define WLAN_RSNX_CAPAB_URNM_MFPR_X20 10
#define WLAN_RSNX_CAPAB_URNM_MFPR 15
+#define WLAN_RSNX_CAPAB_SSID_PROTECTION 21
/* Multiple BSSID element subelements */
#define WLAN_MBSSID_SUBELEMENT_NONTRANSMITTED_BSSID_PROFILE 0
@@ -1451,6 +1452,12 @@
#define QM_IE_OUI_TYPE 0x22
#define WFA_CAPA_IE_VENDOR_TYPE 0x506f9a23
#define WFA_CAPA_OUI_TYPE 0x23
+#define WFA_RSNE_OVERRIDE_OUI_TYPE 0x29
+#define WFA_RSNE_OVERRIDE_2_OUI_TYPE 0x2a
+#define WFA_RSNXE_OVERRIDE_OUI_TYPE 0x2b
+#define RSNE_OVERRIDE_IE_VENDOR_TYPE 0x506f9a29
+#define RSNE_OVERRIDE_2_IE_VENDOR_TYPE 0x506f9a2a
+#define RSNXE_OVERRIDE_IE_VENDOR_TYPE 0x506f9a2b
#define MULTI_AP_SUB_ELEM_TYPE 0x06
#define MULTI_AP_PROFILE_SUB_ELEM_TYPE 0x07
diff --git a/src/common/nan_de.c b/src/common/nan_de.c
index e1999a0..12fad31 100644
--- a/src/common/nan_de.c
+++ b/src/common/nan_de.c
@@ -1209,6 +1209,12 @@
return -1;
}
+ if (!params->unsolicited && !params->solicited) {
+ wpa_printf(MSG_INFO,
+ "NAN: Publish() - both unsolicited and solicited disabled is invalid");
+ return -1;
+ }
+
publish_id = nan_de_get_handle(de);
if (publish_id < 1)
return -1;
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 2a4086b..5dab120 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -32,6 +32,119 @@
};
/**
+ * DOC: TX/RX NSS and chain configurations
+ * This document describes all of the attributes used in the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION to configure the number of
+ * spatial streams (NSS) and the number of chains used for transmitting (TX) and
+ * receiving (RX) the data.
+ *
+ * Global NSS configuration - Applies to all bands (2.4 GHz and 5/6 GHz)
+ * The following attributes are used to dynamically configure the number of
+ * spatial streams to be used for transmitting or receiving the data in the
+ * 2.4 GHz and 5/6 GHz bands. When configured in disconnected state, the
+ * updated configuration will be considered for the immediately following
+ * connection attempt. If the NSS is updated during a connection, the updated
+ * NSS value is notified to the peer using operating mode notification/spatial
+ * multiplexing power save frame. The updated NSS value after the connection
+ * shall not be greater than the one negotiated during the connection. The
+ * driver rejects any such higher value configuration with a failure.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_NSS: Only symmetric NSS configuration
+ * (such as 2X2 or 1X1) can be done using this attribute.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS: Configure NSS for transmitting the data
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS: Configure NSS for receiving the data
+ *
+ * The QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS and QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS
+ * attributes must be defined together or the driver will reject the command
+ * with a failure. They can be used to configure either symmetric NSS
+ * configuration (such as 2X2 or 1X1) or asymmetric configuration (such as 1X2).
+ *
+ * Per band NSS configuration - Applies to the 2.4 GHz or 5/6 GHz band
+ * The following attributes are used to dynamically configure the number of
+ * spatial streams to be used for transmitting or receiving the data in the
+ * 2.4 GHz band or 5/6 GHz band. All these attributes must be defined together
+ * to configure symmetric NSS configuration (such as 1X1 or 2X2) or asymmetric
+ * NSS configuration (such as 1X2). If any of the attributes is missing, the
+ * driver will reject the command with a failure. This configuration is allowed
+ * only when in connected state and will be effective until disconnected. The
+ * NSS value configured after the connection shall not be greater than the value
+ * negotiated during the connection. Any such higher value configuration shall
+ * be treated as invalid configuration by the driver.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS_2GHZ: Configure TX_NSS in 2.4 GHz band
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS_2GHZ: Configure RX_NSS in 2.4 GHz band
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS_5GHZ: Configure TX_NSS in 5 or 6 GHz band
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS_5GHZ: Configure RX_NSS in 5 or 6 GHz band
+ *
+ * Global chain configuration - Applies to all bands (2.4 GHz and 5/6 GHz)
+ * The following attributes are used to dynamically configure the number of
+ * chains to be used for transmitting or receiving the data in the 2.4 GHz and
+ * 5/6 GHz bands. This configuration is allowed only when in connected state
+ * and will be effective until disconnected. The driver rejects this
+ * configuration if the number of spatial streams being used in the current
+ * connection cannot be supported by this configuration.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS: The number of chains to be used
+ * for transmitting the data in both the 2.4 GHz and 5/6 GHz bands.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS: The number of chains to be used
+ * for receiving the data in both the 2.4 GHz and 5/6 GHz bands.
+ *
+ * The attributes QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS and
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS must be defined together or the
+ * driver will reject the command with a failure.
+ *
+ * Per band chain configuration - Applies to the 2.4 GHz or 5/6 GHz band
+ * The following band specific attributes are used to dynamically configure the
+ * number of chains to be used for tranmissting or receiving the data in the
+ * 2.4 GHz or 5/6 GHz band. These attributes must be defined together or the
+ * driver will reject the command. This configuration is allowed only when in
+ * connected state and will be effective until disconnected. The driver rejects
+ * this configuration if the number of spatial streams being used in the
+ * current connection cannot be supported by this configuration.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS_2GHZ: The number of chains to be
+ * used for transmitting the data in the 2.4 GHz band.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS_2GHZ: The number of chains to be
+ * used for receiving the data in the 2.4 GHz band.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS_5GHZ: The number of chains to be
+ * used for transmitting the data in the 5/6 GHz band.
+ * @QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS_5GHZ: The number of chains to be
+ * used for receiving the data in the 5/6 GHz band.
+ *
+ * The following scenarios capture how the driver process the configuration when
+ * different TX/RX NSS and chain config attributes are used in the command.
+ *
+ * Case 1: CONFIG_NSS + CONFIG_TX_NSS/RX_NSS - Only CONFIG_NSS is applied
+ * since only one of the TX_NSS or RX_NSS attribute is present.
+ *
+ * Case 2: CONFIG_NSS + CONFIG_TX_NSS + CONFIG_RX_NSS - Same NSS values are
+ * used to configure TX,RX in both the 2.4 GHz and 5/6 GHz bands.
+ *
+ * Case 3: Case 2 + NUM_TX_CHAINS + NUM_RX_CHAINS - The NSS and the number of
+ * chains values are used to configure TX,RX in both the 2.4 GHz and 5/6 GHz
+ * bands.
+ *
+ * Case 4: TX_NSS_2GHZ/TX_NSS_5GHZ + RX_NSS_2GHZ/RX_NSS_5GHZ - Since per band
+ * TX/RX NSS attribute is missing, the driver rejects the command.
+ *
+ * Case 5: TX_NSS_2GHZ + TX_NSS_5GHZ + RX_NSS_2GHZ + RX_NSS_5GHZ - The 2.4 GHz
+ * band is configured with the TX_NSS_2GHZ, RX_NSS_2GHZ values. The 5/6 GHz band
+ * is configured with the TX_NSS_5GHZ, RX_NSS_5GHZ values.
+ *
+ * Case 6: TX_CHAINS_2GHZ/TX_CHAINS_5GHZ + RX_CHAINS_5GHZ/RX_CHAINS_5GHZ - Since
+ * per band TX/RX chains attribute is missing, the driver rejects the command.
+ *
+ * Case 7: TX_CHAINS_2GHZ + TX_CHAINS_5GHZ + RX_CHAINS_5GHZ + RX_CHAINS_5GHZ -
+ * The 2.4 GHz band is configured with the TX_CHAINS_2GHZ, RX_CHAINS_2GHZ
+ * values. The 5/6 GHz band is configured with the TX_CHAINS_5GHZ,
+ * RX_CHAINS_5GHZ values.
+ *
+ * Case 8: Case 5 + Case 7 - Per band TX,RX NSS and chains are configured.
+ *
+ * Case 9: Case 2 + Case 8 - Per band TX,RX NSS and chains are configured.
+ */
+
+/**
* enum qca_nl80211_vendor_subcmds - QCA nl80211 vendor command identifiers
*
* @QCA_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0
@@ -1057,6 +1170,73 @@
* driver to notify user application about the spectral scan completion.
* The attributes used with this subcommand are defined in
* enum qca_wlan_vendor_attr_spectral_scan_complete.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ASYNC_GET_STATION: Register for receiving
+ * %NL80211_CMD_GET_STATION responses as unicast events when there are
+ * %NL80211_CMD_GET_STATION requests from any userspace module on the same
+ * interface index with which this command is sent. This command is also
+ * used as the unicast event to indicate the %NL80211_CMD_GET_STATION
+ * response. The attributes for this command are defined in
+ * enum qca_wlan_vendor_async_get_station_attr.
+ *
+ * The driver will send the unicast events with same netlink port ID which
+ * is used by userspace application for sending the registration command.
+ * If multiple registration commands are received with different netlink
+ * port IDs, the driver will send unicast events with each netlink port ID
+ * separately.
+ *
+ * Userspace applications can deregister the unicast event reporting with
+ * disable configuration. The registrations will be removed automatically
+ * by the driver when the corresponding netlink socket is closed.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AP_SUSPEND: Vendor command to put an AP interface
+ * in suspend state. On enabling suspend, AP deauthenticates all associated
+ * stations and stops TX/RX operations on the interface. The driver
+ * retains the AP configuration and on resume, starts all TX/RX operations
+ * with the same AP configuration.
+ *
+ * This subcommand is also used as an event to notify userspace about AP
+ * suspended/resumed state changes.
+ *
+ * The attributes used with this command/event are defined in enum
+ * qca_wlan_vendor_attr_ap_suspend.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_FLOW_STATS: Event indication from the driver to
+ * the userspace which contains all the statistics collected for a flow to
+ * be classified. This event is sent if the userspace enables the
+ * flow stats reporting via the command
+ * @QCA_NL80211_VENDOR_SUBCMD_ASYNC_STATS_POLICY and when the driver has
+ * collected the required flow statistics, as specified by the attributes
+ * of this event. The attributes for this event are defined in
+ * enum qca_wlan_vendor_attr_flow_stats.
+ * @QCA_NL80211_VENDOR_SUBCMD_FLOW_CLASSIFY_RESULT: This vendor command is used
+ * to indicate the flow classification result based on the flow samples
+ * received as a part of @QCA_NL80211_VENDOR_SUBCMD_FLOW_STATS. The
+ * attributes for this command are defined in the
+ * enum qca_wlan_vendor_attr_flow_classify_result.
+ * @QCA_NL80211_VENDOR_SUBCMD_ASYNC_STATS_POLICY: This vendor command is used to
+ * indicate the ASYNC statistics policy from the userspace to the driver
+ * and it contains the STATS type for which the command is intended. The
+ * attributes for this command are defined in the
+ * enum qca_wlan_vendor_attr_async_stats_policy.
+ * @QCA_NL80211_VENDOR_SUBCMD_CLASSIFIED_FLOW_REPORT: Event indication from the
+ * driver to the userspace containing all the samples of a classified
+ * flow along with its classification result. This event is sent by the
+ * driver to userspace when it receives classification result via the
+ * command @QCA_NL80211_VENDOR_SUBCMD_FLOW_CLASSIFY_RESULT and the
+ * collection of these statistics has been enabled by the command
+ * @QCA_NL80211_VENDOR_SUBCMD_ASYNC_STATS_POLICY. The attributes for this
+ * event are defined in enum qca_wlan_vendor_attr_flow_stats.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_USD: Vendor subcommand to implement unsynchronized
+ * service discovery (USD). Based on the type of the USD subcommand the USD
+ * operation to publish, subscribe, update publish, cancel publish, or
+ * cancel subscribe is triggered.
+ *
+ * When used as an event, the driver notifies the status of an USD command.
+ *
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_usd.
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -1283,6 +1463,13 @@
QCA_NL80211_VENDOR_SUBCMD_DISASSOC_PEER = 240,
QCA_NL80211_VENDOR_SUBCMD_ADJUST_TX_POWER = 241,
QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_COMPLETE = 242,
+ QCA_NL80211_VENDOR_SUBCMD_ASYNC_GET_STATION = 243,
+ QCA_NL80211_VENDOR_SUBCMD_AP_SUSPEND = 244,
+ QCA_NL80211_VENDOR_SUBCMD_FLOW_STATS = 245,
+ QCA_NL80211_VENDOR_SUBCMD_FLOW_CLASSIFY_RESULT = 246,
+ QCA_NL80211_VENDOR_SUBCMD_ASYNC_STATS_POLICY = 247,
+ QCA_NL80211_VENDOR_SUBCMD_CLASSIFIED_FLOW_REPORT = 248,
+ QCA_NL80211_VENDOR_SUBCMD_USD = 249,
};
/* Compatibility defines for previously used subcmd names.
@@ -1517,9 +1704,23 @@
QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
};
+/**
+ * enum qca_roaming_policy - Represents the policies for roaming. Used by
+ * QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY.
+ *
+ * QCA_ROAMING_NOT_ALLOWED: Roaming is not allowed/disabled.
+ *
+ * QCA_ROAMING_ALLOWED_WITHIN_ESS: Roaming is allowed with in an ESS with
+ * default RSSI thresholds.
+ *
+ * QCA_ROAMING_MODE_AGGRESSIVE: This mode is an extension of
+ * QCA_ROAMING_ALLOWED_WITHIN_ESS. The driver/firmware roams on higher RSSI
+ * thresholds when compared to QCA_ROAMING_ALLOWED_WITHIN_ESS.
+ */
enum qca_roaming_policy {
QCA_ROAMING_NOT_ALLOWED,
QCA_ROAMING_ALLOWED_WITHIN_ESS,
+ QCA_ROAMING_MODE_AGGRESSIVE,
};
/**
@@ -1988,6 +2189,36 @@
* with %QCA_WLAN_VENDOR_ATTR_CONFIG_AP_ALLOWED_FREQ_LIST.
* @QCA_WLAN_VENDOR_FEATURE_ENHANCED_AUDIO_EXPERIENCE_OVER_WLAN: Flag indicates
* that the device supports enhanced audio experience over WLAN feature.
+ * @QCA_WLAN_VENDOR_FEATURE_HT_VHT_TWT_RESPONDER: Flag indicates that the device
+ * in AP mode supports TWT responder mode in HT and VHT modes.
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_RSN_OVERRIDE_STA: Flag indicates that the device
+ * supports RSNE/RSNXE overriding in STA mode.
+ *
+ * For SME offload to the driver case:
+ * - Supplicant should enable RSNO element use only when the driver
+ * indicates this feature flag.
+ * - The driver should enable RSNO element use with the supplicant selected
+ * BSS only when the supplicant sends an RSNO element with an empty
+ * payload in the connect request elements buffer in NL80211_CMD_CONNECT.
+ *
+ * For BSS selection offload to the driver case:
+ * - Supplicant should enable RSNO element use only when the driver
+ * indicates this feature flag.
+ * - Supplicant should always send RSNO elements in the connect request
+ * elements buffer in NL80211_CMD_CONNECT irrespective of whether RSNO
+ * elements are supported by the BSS that the supplicant selected
+ * - The driver should enable RSNO element use only when the supplicant
+ * sends an RSNO element with an empty payload in connect request
+ * elements in NL80211_CMD_CONNECT.
+ * - The driver should remove RSNO elements from the connect request
+ * elements while preparing the (Re)Association Request frame elements
+ * if the driver selects a different BSS which is not advertising RSNO
+ * elements.
+ *
+ * If both SME and BSS selection offload to the driver, BSS selection
+ * offload to the driver case rules shall be applied.
+ *
* @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
*/
enum qca_wlan_vendor_features {
@@ -2015,6 +2246,8 @@
QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_AP = 21,
QCA_WLAN_VENDOR_FEATURE_AP_ALLOWED_FREQ_LIST = 22,
QCA_WLAN_VENDOR_FEATURE_ENHANCED_AUDIO_EXPERIENCE_OVER_WLAN = 23,
+ QCA_WLAN_VENDOR_FEATURE_HT_VHT_TWT_RESPONDER = 24,
+ QCA_WLAN_VENDOR_FEATURE_RSN_OVERRIDE_STA = 25,
NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
};
@@ -2257,13 +2490,15 @@
* @QCA_TSF_SYNC_GET: Initiate TSF capture and return with captured value
* @QCA_TSF_AUTO_REPORT_ENABLE: Used in STA mode only. Once set, the target
* will automatically send TSF report to the host. To query
- * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY, this operation needs to be
- * initiated first.
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY or
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY_JITTER, this operation needs
+ * to be initiated first.
* @QCA_TSF_AUTO_REPORT_DISABLE: Used in STA mode only. Once set, the target
* will not automatically send TSF report to the host. If
* %QCA_TSF_AUTO_REPORT_ENABLE is initiated and
- * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY is not queried anymore, this
- * operation needs to be initiated.
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY or
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY_JITTER is not queried
+ * anymore, this operation needs to be initiated.
* @QCA_TSF_SYNC_START: Start periodic TSF sync feature. The driver periodically
* fetches TSF and host time mapping from the firmware with interval configured
* through the %QCA_WLAN_VENDOR_ATTR_TSF_SYNC_INTERVAL attribute. If the
@@ -2910,21 +3145,9 @@
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_STBC = 69,
- /* 8-bit unsigned value. This attribute is used to dynamically configure
- * the number of spatial streams. When configured in the disconnected
- * state, the updated configuration will be considered for the
- * immediately following connection attempt. If the NSS is updated after
- * the connection, the updated NSS value is notified to the peer using
- * the Operating Mode Notification/Spatial Multiplexing Power Save
- * frame. The updated NSS value after the connection shall not be
- * greater than the one negotiated during the connection. Any such
- * higher value configuration shall be returned with a failure.
- * Only symmetric NSS configuration (such as 2X2 or 1X1) can be done
- * using this attribute. QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS and
- * QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS attributes shall be used to
- * configure the asymmetric NSS configuration (such as 1X2).
- */
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
QCA_WLAN_VENDOR_ATTR_CONFIG_NSS = 70,
+
/* 8-bit unsigned value to configure Optimized Power Management mode:
* Modes are defined by enum qca_wlan_vendor_opm_mode.
*
@@ -2956,21 +3179,10 @@
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE = 72,
- /* 8-bit unsigned value. This attribute is used to dynamically configure
- * the number of chains to be used for transmitting data. This
- * configuration is allowed only when in connected state and will be
- * effective until disconnected. The driver rejects this configuration
- * if the number of spatial streams being used in the current connection
- * cannot be supported by this configuration.
- */
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS = 73,
- /* 8-bit unsigned value. This attribute is used to dynamically configure
- * the number of chains to be used for receiving data. This
- * configuration is allowed only when in connected state and will be
- * effective until disconnected. The driver rejects this configuration
- * if the number of spatial streams being used in the current connection
- * cannot be supported by this configuration.
- */
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS = 74,
/* 8-bit unsigned value to configure ANI setting type.
@@ -3380,6 +3592,100 @@
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_BTM_SUPPORT = 107,
+ /* 16-bit unsigned value to configure client's keep-alive interval in
+ * seconds. The driver will reduce the keep-alive interval to this
+ * configured value if the AP advertises BSS maximum idle period and if
+ * that BSS max idle period is larger than this configured value. If the
+ * AP does not advertise a maximum value, the configured value will be
+ * used as a keep-alive period for unprotected frames.
+ *
+ * This configuration is applicable only during the STA's current
+ * association.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_KEEP_ALIVE_INTERVAL = 108,
+
+ /* 8-bit unsigned value to configure reduced power scan mode.
+ *
+ * This attribute is used to configure the driver to optimize power
+ * during scan. For example, the driver can switch to 1x1 from 2x2 mode
+ * for additional power save.
+ *
+ * 1 - Enable reduced power scan mode.
+ * 0 - Disable reduced power scan mode.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_REDUCED_POWER_SCAN_MODE = 109,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS_2GHZ = 110,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS_2GHZ = 111,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS_5GHZ = 112,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS_5GHZ = 113,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS_2GHZ = 114,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS_2GHZ = 115,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS_5GHZ = 116,
+
+ /* 8-bit unsigned value. Refer to TX/RX NSS and chain configurations */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS_5GHZ = 117,
+
+ /* 16-bit unsigned value. This attribute is used to dynamically
+ * configure the time duration of data stall detection. Unit is
+ * milliseconds. Valid value range is 0 or 10 ms to 10000 ms. If the
+ * value is 0, the previously configured value is cleared. The driver
+ * rejects this configuration if the value is out of range. This
+ * configuration is effective for all connections on the chip. If the
+ * duration is greater than this configuration and consecutive TX no ack
+ * count is greater than
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_CONSECUTIVE_TX_NO_ACK_THRESHOLD,
+ * data stall event is sent to userspace.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_CONSECUTIVE_TX_NO_ACK_DURATION = 118,
+
+ /* 16-bit unsigned value. This attribute is used to dynamically
+ * configure the threshold of data stall detection. Valid value is 0 or
+ * greater than 10. if the value is 0, the previously configured value
+ * is cleared. The driver rejects this configuration if the value is out
+ * of range. This configuration is effective for all connections on the
+ * chip. If consecutive TX no ack count is greater than this
+ * configuration and duration is greater than
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_CONSECUTIVE_TX_NO_ACK_DURATION,
+ * data stall event is sent to userspace.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_CONSECUTIVE_TX_NO_ACK_THRESHOLD = 119,
+
+ /* 8-bit unsigned value to configure the interface offload type
+ *
+ * This attribute is used to configure the interface offload capability.
+ * User can configure software based acceleration, hardware based
+ * acceleration, or a combination of both using this option. More
+ * details on each option is described under the enum definition below.
+ * Uses enum qca_wlan_intf_offload_type for values.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_INTF_OFFLOAD_TYPE = 120,
+
+ /* 8-bit unsigned integer to configure the driver to follow AP's
+ * preference values to select a roam candidate from BTM request.
+ *
+ * This attribute is used to configure the driver to select the roam
+ * candidate based on AP advertised preference values. If not set,
+ * the driver uses its internal scoring algorithm to do the same.
+ *
+ * 1 - STA follows AP's preference values to select a roam candidate
+ * 0 - STA uses internal scoring algorithm to select a roam candidate
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_FOLLOW_AP_PREFERENCE_FOR_CNDS_SELECT = 121,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -7258,6 +7564,9 @@
* for EHT (IEEE 802.11be). Encoding for this attribute follows the
* convention used in the Disabled Subchannel Bitmap field of the EHT Operation
* element.
+ * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_LINK_ID: Mandatory on AP MLD (u8).
+ * Used with command to configure external ACS operation for a specific link
+ * affiliated to an AP MLD.
*/
enum qca_wlan_vendor_attr_external_acs_channels {
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_INVALID = 0,
@@ -7294,6 +7603,7 @@
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0 = 12,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1 = 13,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_PUNCTURE_BITMAP = 14,
+ QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_LINK_ID = 15,
/* keep last */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST,
@@ -10588,10 +10898,13 @@
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESPONDER_PM_MODE: Optional (u8)
* This attribute contains the value of the Responder PM Mode subfield (0 or 1)
- * from TWT response frame.
+ * from TWT response frame. During TWT setup request, this attribute is used to
+ * configure the Responder PM Mode bit in the control field of the TWT element
+ * for broadcast TWT schedule.
* This parameter is used for
* 1. TWT SET Response
* 2. TWT GET Response
+ * 3. TWT SET Request
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_ANNOUNCE_TIMEOUT: Optional (u32)
* This attribute is used to configure the announce timeout value (in us) in
@@ -11001,10 +11314,18 @@
* This attribute configures AC parameters to be used for all TWT
* sessions in AP mode.
* Uses the enum qca_wlan_ac_type values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_UNAVAILABILITY_MODE: Flag attribute,
+ * used by TWT responder to indicate unavailability outside of the SPs.
+ * Enable (flag attribute present) - Indicates that the TWT responder may be
+ * unavailable outside of the SPs of its broadcast TWT schedule.
+ * Disable (flag attribute not present) - Indicates that the responder will be
+ * available for all TWT sessions (including individual TWT).
*/
enum qca_wlan_vendor_attr_twt_set_param {
QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_AP_AC_VALUE = 1,
+ QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_UNAVAILABILITY_MODE = 2,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_AFTER_LAST,
@@ -12508,6 +12829,16 @@
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PAD: Attribute used for padding for
* 64-bit alignment.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY_JITTER: u32, used in STA mode
+ * only. This represents the average of the delta between successive uplink
+ * frames congestion duration in MAC queue in unit of ms. This can be queried
+ * either in connected state or disconnected state.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_NSS_PKT_COUNT: Array of nested attributes,
+ * used in STA mode. This represents the number of MSDU packets
+ * (unicast/multicast/broadcast) transmitted/received with each NSS value. See
+ * enum qca_wlan_vendor_attr_nss_pkt.
*/
enum qca_wlan_vendor_attr_get_sta_info {
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_INVALID = 0,
@@ -12564,6 +12895,8 @@
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PER_MCS_TX_PACKETS = 51,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PER_MCS_RX_PACKETS = 52,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PAD = 53,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY_JITTER = 54,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_NSS_PKT_COUNT = 55,
/* keep last */
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST,
@@ -14425,11 +14758,14 @@
* details for each peer and used in both an event and a command response.
* The nested attributes used inside QCA_WLAN_VENDOR_ATTR_PASN_PEERS are
* defined in enum qca_wlan_vendor_attr_pasn_peer.
+ * @QCA_WLAN_VENDOR_ATTR_PASN_LINK_ID: u8 attribute used to identify a
+ * specific link affiliated to an MLD.
*/
enum qca_wlan_vendor_attr_pasn {
QCA_WLAN_VENDOR_ATTR_PASN_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_PASN_ACTION = 1,
QCA_WLAN_VENDOR_ATTR_PASN_PEERS = 2,
+ QCA_WLAN_VENDOR_ATTR_PASN_LINK_ID = 3,
/* keep last */
QCA_WLAN_VENDOR_ATTR_PASN_AFTER_LAST,
@@ -14489,7 +14825,8 @@
* attribute, holds the LTF keyseed derived from KDK of PASN handshake.
* The length of this attribute is dependent on the value of
* %QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SHA_TYPE.
-
+ * @QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_LINK_ID: This u8 attribute is used
+ * for secure ranging to identify a specific link affiliated to an AP MLD.
*/
enum qca_wlan_vendor_attr_secure_ranging_ctx {
QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_INVALID = 0,
@@ -14500,6 +14837,7 @@
QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_TK = 5,
QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_CIPHER = 6,
QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_LTF_KEYSEED = 7,
+ QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_LINK_ID = 8,
/* keep last */
QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_AFTER_LAST,
@@ -17042,4 +17380,671 @@
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COMPLETE_AFTER_LAST - 1,
};
+/**
+ * enum qca_wlan_vendor_async_get_station_attr - Attribute values for
+ * %QCA_NL80211_VENDOR_SUBCMD_ASYNC_GET_STATION command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_CONFIG: 8-bit unsigned value to
+ * configure the driver to enable/disable reporting
+ * %QCA_NL80211_VENDOR_SUBCMD_ASYNC_GET_STATION events. 1-Enable, 0-Disable.
+ * This is required in a command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_RESPONSE: Nested attribute. This is
+ * required in %QCA_NL80211_VENDOR_SUBCMD_ASYNC_GET_STATION event.
+ * This attribute is nested with the station MAC address in %NL80211_ATTR_MAC
+ * and the station information in %NL80211_ATTR_STA_INFO nested attribute, see
+ * enum nl80211_sta_info.
+ */
+enum qca_wlan_vendor_async_get_station_attr {
+ QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_CONFIG = 1,
+ QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_RESPONSE = 2,
+
+ QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_MAX =
+ QCA_WLAN_VENDOR_ATTR_ASYNC_GET_STATION_AFTER_LAST - 1,
+};
+
+/* enum qca_wlan_vendor_ap_suspend_state - Attribute values for
+ * QCA_WLAN_VENDOR_ATTR_AP_SUSPEND_STATE.
+ *
+ * @QCA_WLAN_VENDOR_AP_SUSPEND_STATE_DISABLE: Disable suspend state. When used
+ * with a command, the driver resumes AP with the same configuration that was
+ * applied earlier and starts all TX/RX operations. When used in an event,
+ * indicates the AP interface resumed.
+ *
+ * @QCA_WLAN_VENDOR_AP_SUSPEND_STATE_ENABLE: Enable suspend state. In this
+ * mode, all associated STAs are disconnected and TX/RX is stopped. While an AP
+ * is in this state, it allows only %QCA_WLAN_VENDOR_AP_SUSPEND_STATE_DISABLE
+ * or AP stop/teardown operations. When used in an event, indicates the AP
+ * interface suspended.
+ */
+enum qca_wlan_vendor_ap_suspend_state {
+ QCA_WLAN_VENDOR_AP_SUSPEND_STATE_DISABLE = 0,
+ QCA_WLAN_VENDOR_AP_SUSPEND_STATE_ENABLE = 1,
+};
+
+/* enum qca_wlan_vendor_attr_ap_suspend - Definition of attributes for
+ * @QCA_NL80211_VENDOR_SUBCMD_AP_SUSPEND to configure/notify the suspend state.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AP_SUSPEND_STATE: u8 attribute to configure/notify
+ * suspend state defined in enum qca_wlan_vendor_ap_suspend_state.
+ */
+enum qca_wlan_vendor_attr_ap_suspend {
+ QCA_WLAN_VENDOR_ATTR_AP_SUSPEND_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_AP_SUSPEND_STATE = 1,
+
+ QCA_WLAN_VENDOR_ATTR_AP_SUSPEND_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_AP_SUSPEND_MAX =
+ QCA_WLAN_VENDOR_ATTR_AP_SUSPEND_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_traffic_type - Traffic types into which the flows can be classified.
+ * @QCA_TRAFFIC_TYPE_STREAMING: Traffic type is streaming
+ * @QCA_TRAFFIC_TYPE_GAMING: Traffic type is gaming
+ * @QCA_TRAFFIC_TYPE_VOICE_CALL: Traffic type is a voice call
+ * @QCA_TRAFFIC_TYPE_VIDEO_CALL: Traffic type is a video call
+ * @QCA_TRAFFIC_TYPE_SCREEN_SHARE: Traffic type is screen share
+ * @QCA_TRAFFIC_TYPE_UNKNOWN: Traffic type is unknown
+ * @QCA_TRAFFIC_TYPE_INVALID: Invalid traffic type
+ */
+enum qca_traffic_type {
+ QCA_TRAFFIC_TYPE_STREAMING = 0,
+ QCA_TRAFFIC_TYPE_GAMING = 1,
+ QCA_TRAFFIC_TYPE_VOICE_CALL = 2,
+ QCA_TRAFFIC_TYPE_VIDEO_CALL = 3,
+ QCA_TRAFFIC_TYPE_SCREEN_SHARE = 4,
+ QCA_TRAFFIC_TYPE_UNKNOWN = 5,
+ QCA_TRAFFIC_TYPE_INVALID = 6,
+};
+
+/**
+ * enum qca_wlan_vendor_flow_tuple_proto - Definition of the values to specify
+ * the flow tuple protocol in QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_PROTOCOL.
+ *
+ * @QCA_WLAN_VENDOR_FLOW_TUPLE_PROTO_UDP: UDP flow
+ *
+ * @QCA_WLAN_VENDOR_FLOW_TUPLE_PROTO_TCP: TCP flow
+ */
+enum qca_wlan_vendor_flow_tuple_proto {
+ QCA_WLAN_VENDOR_FLOW_TUPLE_PROTO_UDP = 0,
+ QCA_WLAN_VENDOR_FLOW_TUPLE_PROTO_TCP = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_flow_tuple - Definition of attributes to specify a
+ * flow tuple.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV4_SRC_ADDR: Optional u32 attribute
+ * indicates the source IPv4 address (in network byte order).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV4_DST_ADDR: Optional u32 attribute
+ * indicates the destination IPv4 address (in network byte order).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV6_SRC_ADDR: Optional NLA_BINARY
+ * attribute of 16 bytes length that indicates the source IPv6 address
+ * (in network byte order) for a flow.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV6_DST_ADDR: Optional NLA_BINARY
+ * attribute of 16 bytes length that indicates the destination IPv6 address
+ * (in network byte order) for a flow.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_SRC_PORT: Mandatory u16 attribute indicates
+ * the TCP/UDP source port.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_DST_PORT: Mandatory u16 attribute indicates
+ * the TCP/UDP destination port.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_PROTOCOL: Mandatory u8 attribute indicates
+ * the flow protocol. Uses the enum qca_wlan_vendor_flow_tuple_proto.
+ *
+ * IPv4 flows have to specify @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV4_SRC_ADDR
+ * and @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV4_DST_ADDR.
+ * IPv6 flows have to specify @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV6_SRC_ADDR
+ * and @QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV6_DST_ADDR.
+ */
+enum qca_wlan_vendor_attr_flow_tuple {
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV4_SRC_ADDR = 1,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV4_DST_ADDR = 2,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV6_SRC_ADDR = 3,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_IPV6_DST_ADDR = 4,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_SRC_PORT = 5,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_DST_PORT = 6,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_PROTOCOL = 7,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_LAST,
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_MAX =
+ QCA_WLAN_VENDOR_ATTR_FLOW_TUPLE_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_txrx_stats - Definition of attributes to specify
+ * TX/RX sample for one window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_STATS_NUM_BYTES: Mandatory u64 attribute indicates
+ * the total number of uplink/downlink bytes within the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_STATS_NUM_PKTS: Mandatory u32 attribute indicates
+ * the total number of packets (uplink/downlink) within the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_SIZE_MIN: Mandatory u32 attribute
+ * indicates the minimum uplink/downlink packet size (in bytes) during the
+ * sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_SIZE_MAX: Mandatory u32 attribute
+ * indicates the maximum uplink/downlink packet size (in bytes) during the
+ * sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_IAT_MIN: Mandatory u64 attribute
+ * indicates the minimum uplink/downlink packet IAT (inter-arrival time)
+ * in microseconds, during the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_IAT_MAX: Mandatory u64 attribute
+ * indicates the maximum uplink/downlink packet IAT (inter-arrival time)
+ * in microseconds, during the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_IAT_SUM: Mandatory u64 attribute
+ * indicates the sum of all the values of uplink/downlink packet IAT
+ * (inter-arrival time) in microseconds, during the sampling window.
+ * This attribute is used to calculate the mean packet (inter-arrival time)
+ * during the sampling window.
+ */
+enum qca_wlan_vendor_attr_txrx_stats {
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_NUM_BYTES = 1,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_NUM_PKTS = 2,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_SIZE_MIN = 3,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_SIZE_MAX = 4,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_IAT_MIN = 5,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_IAT_MAX = 6,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_PKT_IAT_SUM = 7,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_LAST,
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_TXRX_STATS_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_txrx_samples_windows - Definition of attributes
+ * to specify the TX/RX statistics collected in a sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_WINDOW_SIZE: Mandatory u32
+ * attribute indicates window size for packet TX/RX sampling (in milliseconds).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_UL_STATS: Mandatory nested
+ * attribute containing the uplink TX/RX packet statistics for a flow. Uses the
+ * enum qca_wlan_vendor_attr_txrx_stats.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_DL_STATS: Mandatory nested
+ * attribute containing the downlink TX/RX packet statistics for a flow. Uses
+ * the enum qca_wlan_vendor_attr_txrx_stats.
+ */
+enum qca_wlan_vendor_attr_txrx_samples_windows {
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_WINDOW_SIZE = 1,
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_UL_STATS = 2,
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_DL_STATS = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_LAST,
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_MAX =
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_txrx_samples - Definition of attributes to specify
+ * a TX/RX sample.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS: Mandatory array of nested
+ * attributes that indicates the TX/RX samples in multiple overlapping windows.
+ * This uses the attributes defined by
+ * enum qca_wlan_vendor_attr_txrx_samples_windows.
+ */
+enum qca_wlan_vendor_attr_txrx_samples {
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_WINDOWS = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_LAST,
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_MAX =
+ QCA_WLAN_VENDOR_ATTR_TXRX_SAMPLES_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_burst_stats - Definition of attribute to specify
+ * burst statistics.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_DURATION_MIN: Mandatory u32 attribute
+ * indicates minimum burst duration (in milliseconds) during the sampling
+ * window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_DURATION_MAX: Mandatory u32 attribute
+ * indicates maximum burst duration (in milliseconds) during the sampling
+ * window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_DURATION_SUM: Mandatory u64 attribute
+ * indicates the sum of all the values of burst duration (in milliseconds)
+ * during the sampling window. This attribute is used to calculate the mean
+ * burst duration (in milliseconds) during the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_SIZE_MIN: Mandatory u64 attribute
+ * indicates minimum burst size (in bytes) during the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_SIZE_MAX: Mandatory u64 attribute
+ * indicates maximum burst size (in bytes) during the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_SIZE_SUM: Mandatory u64 attribute
+ * indicates the sum of all the values of burst size (in bytes) during the
+ * sampling window. This attribute is used to calculate the mean burst size
+ * (in bytes) during the sampling window.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_COUNT: Mandatory u32 attribute
+ * indicates the number of bursts during the sampling window.
+ */
+enum qca_wlan_vendor_attr_burst_stats {
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_DURATION_MIN = 1,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_DURATION_MAX = 2,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_DURATION_SUM = 3,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_SIZE_MIN = 4,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_SIZE_MAX = 5,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_SIZE_SUM = 6,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_BURST_COUNT = 7,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_LAST,
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_BURST_STATS_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_burst_sample - Definition of attributes to specify
+ * a burst sample.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_TXRX_STATS: Mandatory nested attribute
+ * indicates the uplink and downlink packet statistics collected in a
+ * sampling window, containing attributes defined in
+ * enum qca_wlan_vendor_attr_txrx_samples_windows.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_UL_BURST_STATS: Optional nested attribute
+ * indicates the uplink burst stats, containing attributes defined in
+ * enum qca_wlan_vendor_attr_burst_stats.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_DL_BURST_STATS: Optional nested attribute
+ * indicates the downlink burst stats, containing attributes defined in
+ * enum qca_wlan_vendor_attr_burst_stats.
+ */
+enum qca_wlan_vendor_attr_burst_sample {
+ QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_TXRX_STATS = 1,
+ QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_UL_BURST_STATS = 2,
+ QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_DL_BURST_STATS = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_LAST,
+ QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_MAX =
+ QCA_WLAN_VENDOR_ATTR_BURST_SAMPLES_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_flow_stats - Definition of attribute used by
+ * %QCA_NL80211_VENDOR_SUBCMD_CLASSIFIED_FLOW_REPORT and
+ * %QCA_NL80211_VENDOR_SUBCMD_FLOW_STATS.
+ *
+ * Presence of one of the attributes
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_STATS_TXRX_SAMPLES and
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_STATS_BURST_SAMPLES is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_STATS_FLOW_TUPLE: Mandatory nested attribute
+ * containing the flow tuple of the flow for which the statistics are being
+ * reported.
+ * Uses the attributes defined by enum qca_wlan_vendor_attr_flow_tuple.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_STATS_TRAFFIC_TYPE: Optional u8 attribute
+ * indicates the traffic type classified for this flow tuple. Uses the
+ * enum qca_traffic_type values.
+ * This attribute is mandatory for the command
+ * @QCA_NL80211_VENDOR_SUBCMD_CLASSIFIED_FLOW_REPORT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_STATS_TXRX_SAMPLES: Optional nested attribute
+ * containing nested array of TX/RX samples defined in
+ * enum qca_wlan_vendor_attr_txrx_samples.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_STATS_BURST_SAMPLES: Optional nested attribute
+ * indicates the packet burst statistics for a flow. Uses attributes defined by
+ * enum qca_wlan_vendor_attr_burst_sample.
+ */
+enum qca_wlan_vendor_attr_flow_stats {
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_FLOW_TUPLE = 1,
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_TRAFFIC_TYPE = 2,
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_TXRX_SAMPLES = 3,
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_BURST_SAMPLES = 4,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_LAST,
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_FLOW_STATS_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_flow_classify_result - Definition of attributes to
+ * specify the flow classification result. This enum is used by
+ * @QCA_NL80211_VENDOR_SUBCMD_FLOW_CLASSIFY_RESULT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_FLOW_TUPLE: Mandatory nested
+ * attribute containing attributes defined by
+ * enum qca_wlan_vendor_attr_flow_tuple.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_TRAFFIC_TYPE: Mandatory u8
+ * attribute indicates the traffic type learned for this flow tuple. Uses the
+ * enum qca_traffic_type values.
+ */
+enum qca_wlan_vendor_attr_flow_classify_result {
+ QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_FLOW_TUPLE = 1,
+ QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_TRAFFIC_TYPE = 2,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_LAST,
+ QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_MAX =
+ QCA_WLAN_VENDOR_ATTR_FLOW_CLASSIFY_RESULT_LAST - 1,
+};
+
+/**
+ * enum qca_async_stats_sub_module - The statistics type used in async
+ * statistics policy.
+ * Used by @QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_STATS_TYPE.
+ *
+ * @QCA_ASYNC_STATS_TYPE_POWERSAVE: Wi-Fi powersave statistics
+ *
+ * @QCA_ASYNC_STATS_TYPE_FLOW_STATS: Flow statistics
+ *
+ * @QCA_ASYNC_STATS_TYPE_CLASSIFIED_FLOW_STATS: Classified flow statistics
+ */
+enum qca_async_stats_type {
+ QCA_ASYNC_STATS_TYPE_POWERSAVE = 0,
+ QCA_ASYNC_STATS_TYPE_FLOW_STATS = 1,
+ QCA_ASYNC_STATS_TYPE_CLASSIFIED_FLOW_STATS = 2,
+};
+
+/**
+ * enum qca_async_stats_action - ASYNC statistics action. Used by
+ * @QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_ACTION.
+ *
+ * @QCA_ASYNC_STATS_ACTION_START: Start indication for async statistics
+ * collection.
+ * @QCA_ASYNC_STATS_ACTION_STOP: Stop indication for async statistics
+ * collection.
+ */
+enum qca_async_stats_action {
+ QCA_ASYNC_STATS_ACTION_START = 0,
+ QCA_ASYNC_STATS_ACTION_STOP = 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_async_stats_policy - Definition of attributes to
+ * specify the ASYNC statistics policy. This enum is used by
+ * @QCA_NL80211_VENDOR_SUBCMD_ASYNC_STATS_POLICY.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_STATS_TYPE: Mandatory u8
+ * attribute indicates the statistics type for which the async statistics policy
+ * needs to be applied by the driver. Uses the enum qca_async_stats_type values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_ACTION: Mandatory u8 attribute
+ * indicates the action as part of this policy.
+ * Uses the enum qca_async_stats_action values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_STATS_PERIODICITY: Optional u32
+ * attribute indicates the periodicity (in milliseconds) for the statistics to
+ * be reported. This attribute is mandatory for QCA_ASYNC_STATS_TYPE_POWERSAVE.
+ */
+enum qca_wlan_vendor_attr_async_stats_policy {
+ QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_STATS_TYPE = 1,
+ QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_ACTION = 2,
+ QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_STATS_PERIODICITY = 3,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_LAST,
+ QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_MAX =
+ QCA_WLAN_VENDOR_ATTR_ASYNC_STATS_POLICY_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_nss_pkt - Attributes used by
+ * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_NSS_PKT_COUNT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_NSS_PKT_NSS_VALUE: u8 attribute. This
+ * represents the number of spatial streams.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_NSS_PKT_TX_PACKET_COUNT: u64 attribute. This
+ * represents the number of MSDU packets transmitted with the number of spatial
+ * streams specified in %QCA_WLAN_VENDOR_ATTR_NSS_PKT_NSS_VALUE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_NSS_PKT_RX_PACKET_COUNT: u64 attribute. This
+ * represents the number of MSDU packets received with the number of spatial
+ * streams specified in %QCA_WLAN_VENDOR_ATTR_NSS_PKT_NSS_VALUE.
+ */
+enum qca_wlan_vendor_attr_nss_pkt {
+ QCA_WLAN_VENDOR_ATTR_NSS_PKT_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_NSS_PKT_NSS_VALUE = 1,
+ QCA_WLAN_VENDOR_ATTR_NSS_PKT_TX_PACKET_COUNT = 2,
+ QCA_WLAN_VENDOR_ATTR_NSS_PKT_RX_PACKET_COUNT = 3,
+
+ QCA_WLAN_VENDOR_ATTR_NSS_PKT_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_NSS_PKT_MAX =
+ QCA_WLAN_VENDOR_ATTR_NSS_PKT_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_intf_offload_type - Definition of available values for
+ * QCA_WLAN_VENDOR_ATTR_CONFIG_INTF_OFFLOAD_TYPE to specify the offload path for
+ * packets handled through a network device.
+ *
+ * There are three offload paths possible for handling packet forwarding between
+ * Ethernet and Wi-Fi network, and which path to use can be configured on a per
+ * netdevice level based on use case. Userspace can choose different options
+ * based on use cases like performance requirements, traffic control features
+ * and limitations provided in each option.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_INTF_OFFLOAD_TYPE_NONE: No acceleration configured.
+ * Packets are processed through the Linux kernel networking stack.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_INTF_OFFLOAD_TYPE_SFE: Packets are processed through
+ * the shortcut forwarding engine (SFE) to bypass the Linux networking stack
+ * for improved throughput performance. This option is applicable for AP, STA,
+ * and Mesh mode and available for all radio designs. From the performance
+ * aspect, this option consumes more CPU compared to the other two options.
+ * Linux traffic control can be further applied with this option to have more
+ * control on the traffic flows.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_INTF_OFFLOAD_TYPE_ACTIVE_VP: Packets are processed
+ * through both hardware and software in this case. Packet classification is
+ * done by the hardware and then the packets are delivered to software along
+ * with classification results as meta data. Software can choose to do more
+ * classification/QoS based on use cases. This is applicable for AP, STA, and
+ * Mesh modes and this is available for all radio designs. From the performance
+ * aspect, this option consumes relatively less CPU compared to the SFE option
+ * above. Linux traffic control rules cannot be applied with this option.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_INTF_OFFLOAD_TYPE_PPE_DS: Packets are processed through
+ * special hardware (Direct Switch) rings which can directly forward the packets
+ * between Ethernet hardware and Wi-Fi hardware with very little software
+ * involvement. This is applicable only for AP and STA modes; not applicable
+ * for Mesh mode. From the performance aspect, this option consumes very much
+ * less CPU compared to the other options. Linux traffic control rules cannot be
+ * applied when this option is used. This option is applicable only for
+ * specific radio designs. When this option is not available, the default option
+ * (SFE) would be configured.
+ */
+enum qca_wlan_intf_offload_type {
+ QCA_WLAN_INTF_OFFLOAD_TYPE_NONE = 0,
+ QCA_WLAN_INTF_OFFLOAD_TYPE_SFE = 1,
+ QCA_WLAN_INTF_OFFLOAD_TYPE_ACTIVE_VP = 2,
+ QCA_WLAN_INTF_OFFLOAD_TYPE_PPE_DS = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_usd_op_type: Attribute values for
+ * %QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE to the vendor subcmd
+ * %QCA_NL80211_VENDOR_SUBCMD_USD. This is a mandatory u8 attribute which
+ * represents the USD command type.
+ *
+ * @QCA_WLAN_VENDOR_USD_OP_TYPE_FLUSH: Indicates USD tear down of all active
+ * publish and subscribe sessions.
+ *
+ * @QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH: Indicates USD solicited publish
+ * operation that enables to offer a service for other devices based on
+ * given parameters.
+ *
+ * @QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE: Indicates USD active subscribe
+ * operation that requests for a given service with given parameters from
+ * other devices that offer the service.
+ *
+ * @QCA_WLAN_VENDOR_USD_OP_TYPE_UPDATE_PUBLISH: Indicates update of an instance
+ * of the publish function of given publish id.
+ *
+ * @QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_PUBLISH: Indicates cancellation of an
+ * instance of the publish function.
+ *
+ * @QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_SUBSCRIBE: Indicates cancellation of an
+ * instance of the subscribe function.
+ */
+enum qca_wlan_vendor_attr_an_usd_op_type {
+ QCA_WLAN_VENDOR_USD_OP_TYPE_FLUSH = 0,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH = 1,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE = 2,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_UPDATE_PUBLISH = 3,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_PUBLISH = 4,
+ QCA_WLAN_VENDOR_USD_OP_TYPE_CANCEL_SUBSCRIBE = 5,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_usd_service_protocol_type: Attribute values for
+ * %QCA_WLAN_VENDOR_ATTR_USD_SERVICE_PROTOCOL_TYPE to the vendor subcmd
+ * %QCA_NL80211_VENDOR_SUBCMD_USD. This is a u8 attribute which represents the
+ * USD service protocol type for service specific information.
+ *
+ * @QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_BONJOUR: Indicates SSI info is
+ * of type Bonjour
+ * @QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_GENERIC: Indicates SSI info is
+ * of type generic
+ * @QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_CSA_MATTER: Indicates SSI info
+ * is of type CSA/Matter
+ */
+enum qca_wlan_vendor_attr_usd_service_protocol_type {
+ QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_BONJOUR = 1,
+ QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_GENERIC = 2,
+ QCA_WLAN_VENDOR_USD_SERVICE_PROTOCOL_TYPE_CSA_MATTER = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_usd_chan_config - Attributes used inside nested
+ * attribute %QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_DEFAULT_FREQ: Required
+ * u32 attribute containing the default channel frequency (MHz).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_FREQ_LIST: Optional array of channel
+ * frequencies in MHz (u32) to publish or subscribe.
+ */
+enum qca_wlan_vendor_attr_usd_chan_config {
+ QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_DEFAULT_FREQ = 1,
+ QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_FREQ_LIST = 2,
+
+ QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_MAX =
+ QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_usd_status
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_STATUS_SUCCESS: USD request success status.
+ * @QCA_WLAN_VENDOR_ATTR_USD_STATUS_FAILED: USD request failed status.
+ */
+enum qca_wlan_vendor_attr_usd_status {
+ QCA_WLAN_VENDOR_ATTR_USD_STATUS_SUCCESS,
+ QCA_WLAN_VENDOR_ATTR_USD_STATUS_FAILED,
+};
+
+/* enum qca_wlan_vendor_attr_usd: Attributes used by vendor command
+ * %QCA_NL80211_VENDOR_SUBCMD_USD.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_SRC_ADDR: 6-byte source MAC address
+ * Mandatory attribute used with type
+ * %QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH and
+ * %QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE: Required u8 attribute.
+ * It indicates the type of the USD command. It uses values defined in enum
+ * qca_wlan_vendor_attr_usd_op_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID: Required u8 attribute.
+ * It contains the publisher/subscribe id that is specific to the
+ * publish/subscribe instance.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_SERVICE_ID: Required 6-byte attribute.
+ * It contains the service id that is specific to the service being
+ * published/subscribed.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_SERVICE_PROTOCOL_TYPE: u8 attribute that indicates
+ * the service protocol type of service specific info. It uses values
+ * defined in enum qca_wlan_vendor_attr_usd_service_protocol_type. It is
+ * applicable when %QCA_WLAN_VENDOR_ATTR_USD_SSI is present.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_SSI: u8 array containing service specific
+ * information that has to be conveyed in publish/subscribe message.
+ * Optional attribute used with type
+ * %QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH,
+ * %QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE, and
+ * %QCA_WLAN_VENDOR_USD_OP_TYPE_UPDATE_PUBLISH.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG: Nested attribute containing USD
+ * channel configuration parameters.
+ * Required for type %QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH and
+ * %QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE.
+ * See enum qca_wlan_vendor_attr_usd_chan_config for nested attributes.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_ELEMENT_CONTAINER: u8 array containing a USD
+ * element container buffer that has to be conveyed in publish/subscribe
+ * message.
+ * Required for type %QCA_WLAN_VENDOR_USD_OP_TYPE_PUBLISH and
+ * %QCA_WLAN_VENDOR_USD_OP_TYPE_SUBSCRIBE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_TTL: u16 attribute. Indicates the timeout
+ * for each request in seconds. Timeout value 0 represents single time
+ * operation.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_USD_STATUS: u8 attribute. Status received in event
+ * indicating whether the underlying driver/firmware has started the USD
+ * operation as indicated by attributes
+ * %QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE and
+ * %QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID.
+ * enum qca_wlan_vendor_attr_usd_status indicates status values.
+ */
+enum qca_wlan_vendor_attr_usd {
+ QCA_WLAN_VENDOR_ATTR_USD_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_USD_SRC_ADDR = 1,
+ QCA_WLAN_VENDOR_ATTR_USD_OP_TYPE = 2,
+ QCA_WLAN_VENDOR_ATTR_USD_INSTANCE_ID = 3,
+ QCA_WLAN_VENDOR_ATTR_USD_SERVICE_ID = 4,
+ QCA_WLAN_VENDOR_ATTR_USD_SERVICE_PROTOCOL_TYPE = 5,
+ QCA_WLAN_VENDOR_ATTR_USD_SSI = 6,
+ QCA_WLAN_VENDOR_ATTR_USD_CHAN_CONFIG = 7,
+ QCA_WLAN_VENDOR_ATTR_USD_ELEMENT_CONTAINER = 8,
+ QCA_WLAN_VENDOR_ATTR_USD_TTL = 9,
+ QCA_WLAN_VENDOR_ATTR_USD_STATUS = 10,
+
+ QCA_WLAN_VENDOR_ATTR_USD_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_USD_MAX =
+ QCA_WLAN_VENDOR_ATTR_USD_AFTER_LAST - 1,
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index f1c164e..a65da61 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -2103,8 +2103,11 @@
wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
*pos, end - *pos);
- if (!sae_is_rejected_groups_elem(*pos, end))
+ if (!sae_is_rejected_groups_elem(*pos, end)) {
+ wpabuf_free(sae->tmp->peer_rejected_groups);
+ sae->tmp->peer_rejected_groups = NULL;
return WLAN_STATUS_SUCCESS;
+ }
epos = *pos;
epos++; /* skip IE type */
@@ -2113,6 +2116,12 @@
return WLAN_STATUS_UNSPECIFIED_FAILURE;
epos++; /* skip ext ID */
len--;
+ if (len & 1) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Invalid length of the Rejected Groups element payload: %u",
+ len);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
wpabuf_free(sae->tmp->peer_rejected_groups);
sae->tmp->peer_rejected_groups = wpabuf_alloc(len);
@@ -2196,6 +2205,9 @@
res = sae_parse_rejected_groups(sae, &pos, end);
if (res != WLAN_STATUS_SUCCESS)
return res;
+ } else {
+ wpabuf_free(sae->tmp->peer_rejected_groups);
+ sae->tmp->peer_rejected_groups = NULL;
}
/* Optional Anti-Clogging Token Container element */
diff --git a/src/common/sae.h b/src/common/sae.h
index c446da3..a353aa8 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -82,6 +82,8 @@
bool omit_pk_elem;
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_SAE_PK */
+
+ struct os_reltime disabled_until;
};
struct sae_pt {
diff --git a/src/common/version.h b/src/common/version.h
index 4409e1c..9e0fbc0 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -9,6 +9,6 @@
#define GIT_VERSION_STR_POSTFIX ""
#endif /* GIT_VERSION_STR_POSTFIX */
-#define VERSION_STR "2.11-devel" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
+#define VERSION_STR "2.12-devel" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
#endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 6ea3311..8eb4a1d 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1031,9 +1031,6 @@
const u8 *end, *pos;
u8 link_id;
- parse->ftie = ie;
- parse->ftie_len = ie_len;
-
pos = opt;
end = ie + ie_len;
wpa_hexdump(MSG_DEBUG, "FT: Parse FTE subelements", pos, end - pos);
@@ -1339,6 +1336,11 @@
}
if (res < 0)
goto fail;
+
+ /* FTE might be fragmented. If it is, the separate Fragment
+ * elements are included in MIC calculation as full elements. */
+ parse->ftie = fte;
+ parse->ftie_len = fte_len;
}
if (prot_ie_count == 0)
@@ -1888,6 +1890,14 @@
data->has_group = 1;
data->key_mgmt = WPA_KEY_MGMT_OSEN;
data->proto = WPA_PROTO_OSEN;
+ } else if (rsn_ie_len >= 2 + 4 + 2 && rsn_ie[1] >= 4 + 2 &&
+ rsn_ie[1] == rsn_ie_len - 2 &&
+ (WPA_GET_BE32(&rsn_ie[2]) == RSNE_OVERRIDE_IE_VENDOR_TYPE ||
+ WPA_GET_BE32(&rsn_ie[2]) ==
+ RSNE_OVERRIDE_2_IE_VENDOR_TYPE) &&
+ WPA_GET_LE16(&rsn_ie[2 + 4]) == RSN_VERSION) {
+ pos = rsn_ie + 2 + 4 + 2;
+ left = rsn_ie_len - 2 - 4 - 2;
} else {
const struct rsn_ie_hdr *hdr;
@@ -3743,6 +3753,11 @@
ie->supp_oper_classes = pos + 2;
ie->supp_oper_classes_len = pos[1];
}
+ } else if (*pos == WLAN_EID_SSID) {
+ ie->ssid = pos + 2;
+ ie->ssid_len = pos[1];
+ wpa_hexdump_ascii(MSG_DEBUG, "RSN: SSID in EAPOL-Key",
+ ie->ssid, ie->ssid_len);
} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
ret = wpa_parse_generic(pos, ie);
if (ret == 1) {
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 01efeea..1e31368 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -17,6 +17,7 @@
#define PMK_LEN_SUITE_B_192 48
#define PMK_LEN_MAX 64
#define WPA_REPLAY_COUNTER_LEN 8
+#define RSN_PN_LEN 6
#define WPA_NONCE_LEN 32
#define WPA_KEY_RSC_LEN 8
#define WPA_GMK_LEN 32
@@ -338,52 +339,54 @@
} STRUCT_PACKED;
+#define KDE_HDR_LEN (1 + 1 + RSN_SELECTOR_LEN)
+
struct rsn_error_kde {
be16 mui;
be16 error_type;
} STRUCT_PACKED;
-#define WPA_IGTK_KDE_PREFIX_LEN (2 + 6)
+#define WPA_IGTK_KDE_PREFIX_LEN (2 + RSN_PN_LEN)
struct wpa_igtk_kde {
u8 keyid[2];
- u8 pn[6];
+ u8 pn[RSN_PN_LEN];
u8 igtk[WPA_IGTK_MAX_LEN];
} STRUCT_PACKED;
-#define WPA_BIGTK_KDE_PREFIX_LEN (2 + 6)
+#define WPA_BIGTK_KDE_PREFIX_LEN (2 + RSN_PN_LEN)
struct wpa_bigtk_kde {
u8 keyid[2];
- u8 pn[6];
+ u8 pn[RSN_PN_LEN];
u8 bigtk[WPA_BIGTK_MAX_LEN];
} STRUCT_PACKED;
-#define RSN_MLO_GTK_KDE_PREFIX_LENGTH (1 + 6)
+#define RSN_MLO_GTK_KDE_PREFIX_LENGTH (1 + RSN_PN_LEN)
#define RSN_MLO_GTK_KDE_PREFIX0_KEY_ID_MASK 0x03
#define RSN_MLO_GTK_KDE_PREFIX0_TX 0x04
#define RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_SHIFT 4
#define RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_MASK 0xF0
-#define RSN_MLO_IGTK_KDE_PREFIX_LENGTH (2 + 6 + 1)
+#define RSN_MLO_IGTK_KDE_PREFIX_LENGTH (2 + RSN_PN_LEN + 1)
#define RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_SHIFT 4
#define RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_MASK 0xF0
struct rsn_mlo_igtk_kde {
u8 keyid[2];
- u8 pn[6];
+ u8 pn[RSN_PN_LEN];
u8 prefix8;
u8 igtk[WPA_IGTK_MAX_LEN];
} STRUCT_PACKED;
-#define RSN_MLO_BIGTK_KDE_PREFIX_LENGTH (2 + 6 + 1)
+#define RSN_MLO_BIGTK_KDE_PREFIX_LENGTH (2 + RSN_PN_LEN + 1)
#define RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_SHIFT 4
#define RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_MASK 0xF0
struct rsn_mlo_bigtk_kde {
u8 keyid[2];
- u8 pn[6];
+ u8 pn[RSN_PN_LEN];
u8 prefix8;
u8 bigtk[WPA_BIGTK_MAX_LEN];
} STRUCT_PACKED;
-#define RSN_MLO_LINK_KDE_FIXED_LENGTH (1 + 6)
+#define RSN_MLO_LINK_KDE_FIXED_LENGTH (1 + ETH_ALEN)
#define RSN_MLO_LINK_KDE_LINK_INFO_INDEX 0
#define RSN_MLO_LINK_KDE_LI_LINK_ID_SHIFT 0
#define RSN_MLO_LINK_KDE_LI_LINK_ID_MASK 0x0F
@@ -695,6 +698,8 @@
size_t supp_channels_len;
const u8 *supp_oper_classes;
size_t supp_oper_classes_len;
+ const u8 *ssid;
+ size_t ssid_len;
u8 qosinfo;
u16 aid;
const u8 *wmm;
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 069e741..8be4012 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1393,6 +1393,23 @@
u8 rfkill_release;
};
+struct unsol_bcast_probe_resp {
+ /**
+ * Unsolicited broadcast Probe Response interval in TUs
+ */
+ unsigned int unsol_bcast_probe_resp_interval;
+
+ /**
+ * Unsolicited broadcast Probe Response template data
+ */
+ u8 *unsol_bcast_probe_resp_tmpl;
+
+ /**
+ * Unsolicited broadcast Probe Response template length
+ */
+ size_t unsol_bcast_probe_resp_tmpl_len;
+};
+
struct wpa_driver_ap_params {
/**
* head - Beacon head from IEEE 802.11 header to IEs before TIM IE
@@ -1736,21 +1753,6 @@
size_t fd_frame_tmpl_len;
/**
- * Unsolicited broadcast Probe Response interval in TUs
- */
- unsigned int unsol_bcast_probe_resp_interval;
-
- /**
- * Unsolicited broadcast Probe Response template data
- */
- u8 *unsol_bcast_probe_resp_tmpl;
-
- /**
- * Unsolicited broadcast Probe Response template length
- */
- size_t unsol_bcast_probe_resp_tmpl_len;
-
- /**
* mbssid_tx_iface - Transmitting interface of the MBSSID set
*/
const char *mbssid_tx_iface;
@@ -1816,6 +1818,9 @@
*/
u8 **rnr_elem_offset;
+ /* Unsolicited broadcast Probe Response data */
+ struct unsol_bcast_probe_resp ubpr;
+
/**
* allowed_freqs - List of allowed 20 MHz channel center frequencies in
* MHz for AP operation. Drivers which support this parameter will
@@ -2330,6 +2335,10 @@
#define WPA_DRIVER_FLAGS2_OWE_OFFLOAD_AP 0x0000000000080000ULL
/** Driver support AP SAE authentication offload */
#define WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP 0x0000000000100000ULL
+/** Driver supports TWT responder in HT and VHT modes */
+#define WPA_DRIVER_FLAGS2_HT_VHT_TWT_RESPONDER 0x0000000000200000ULL
+/** Driver supports RSN override elements */
+#define WPA_DRIVER_FLAGS2_RSN_OVERRIDE_STA 0x0000000000400000ULL
u64 flags2;
#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -2732,6 +2741,7 @@
* @counter_offset_presp: Offset to the count field in probe resp.
* @punct_bitmap - Preamble puncturing bitmap
* @link_id: Link ID to determine the link for MLD; -1 for non-MLD
+ * @ubpr: Unsolicited broadcast Probe Response frame data
*/
struct csa_settings {
u8 cs_count;
@@ -2746,6 +2756,8 @@
u16 punct_bitmap;
int link_id;
+
+ struct unsol_bcast_probe_resp ubpr;
};
/**
@@ -2757,6 +2769,8 @@
* @beacon_after: Next Beacon/Probe Response/(Re)Association Response frame info
* @counter_offset_beacon: Offset to the count field in Beacon frame tail
* @counter_offset_presp: Offset to the count field in Probe Response frame
+ * @ubpr: Unsolicited broadcast Probe Response frame data
+ * @link_id: If >= 0 indicates the link of the AP MLD to configure
*/
struct cca_settings {
u8 cca_count;
@@ -2767,6 +2781,10 @@
u16 counter_offset_beacon;
u16 counter_offset_presp;
+
+ struct unsol_bcast_probe_resp ubpr;
+
+ int link_id;
};
/* TDLS peer capabilities for send_tdls_mgmt() */
@@ -3596,13 +3614,15 @@
/**
* flush - Flush all association stations (AP only)
* @priv: Private driver interface data
+ * @link_id: In case of MLO, valid link ID on which all associated
+ * stations will be flushed, -1 otherwise.
* Returns: 0 on success, -1 on failure
*
* This function requests the driver to disassociate all associated
* stations. This function does not need to be implemented if the
* driver does not process association frames internally.
*/
- int (*flush)(void *priv);
+ int (*flush)(void *priv, int link_id);
/**
* set_generic_elem - Add IEs into Beacon/Probe Response frames (AP)
@@ -6364,6 +6384,14 @@
void *drv_priv;
/**
+ * ctx - Pointer to store ctx of private BSS information
+ *
+ * If not set to NULL, this is used for forwarding the packet
+ * to right link BSS of ML BSS.
+ */
+ void *ctx;
+
+ /**
* freq - Frequency (in MHz) on which the frame was received
*/
int freq;
@@ -6783,6 +6811,7 @@
*/
struct bss_color_collision {
u64 bitmap;
+ int link_id;
} bss_color_collision;
/**
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index ae7f0e5..7186330 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -632,7 +632,7 @@
static int
-atheros_flush(void *priv)
+atheros_flush(void *priv, int link_id)
{
u8 allsta[IEEE80211_ADDR_LEN];
os_memset(allsta, 0xff, IEEE80211_ADDR_LEN);
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 850637f..82d8a01 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -946,7 +946,7 @@
static int
-bsd_flush(void *priv)
+bsd_flush(void *priv, int link_id)
{
u8 allsta[IEEE80211_ADDR_LEN];
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
index d3520aa..3aa5860 100644
--- a/src/drivers/driver_hostap.c
+++ b/src/drivers/driver_hostap.c
@@ -572,7 +572,7 @@
}
-static int hostap_flush(void *priv)
+static int hostap_flush(void *priv, int link_id)
{
struct hostap_driver_data *drv = priv;
struct prism2_hostapd_param param;
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 194a5cd..b2b909e 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -2482,12 +2482,20 @@
}
-static int nl80211_register_action_frame(struct i802_bss *bss,
- const u8 *match, size_t match_len)
+static int nl80211_register_action_frame2(struct i802_bss *bss,
+ const u8 *match, size_t match_len,
+ bool multicast)
{
u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
return nl80211_register_frame(bss, bss->nl_mgmt,
- type, match, match_len, false);
+ type, match, match_len, multicast);
+}
+
+
+static int nl80211_register_action_frame(struct i802_bss *bss,
+ const u8 *match, size_t match_len)
+{
+ return nl80211_register_action_frame2(bss, match, match_len, false);
}
@@ -2563,11 +2571,15 @@
ret = -1;
#endif /* CONFIG_P2P */
#ifdef CONFIG_NAN_USD
+#define NAN_PUB_ACTION ((u8 *) "\x04\x09\x50\x6f\x9a\x13")
/* NAN SDF Public Action */
- if (nl80211_register_action_frame(bss,
- (u8 *) "\x04\x09\x50\x6f\x9a\x13",
- 6) < 0)
- ret = -1;
+ if (nl80211_register_action_frame2(bss, NAN_PUB_ACTION, 6, true) < 0) {
+ /* fallback to non-multicast */
+ if (nl80211_register_action_frame2(bss, NAN_PUB_ACTION, 6,
+ false) < 0)
+ ret = -1;
+ }
+#undef NAN_PUB_ACTION
#endif /* CONFIG_NAN_USD */
#ifdef CONFIG_DPP
/* DPP Public Action */
@@ -4833,7 +4845,7 @@
static int nl80211_unsol_bcast_probe_resp(struct i802_bss *bss,
struct nl_msg *msg,
- struct wpa_driver_ap_params *params)
+ struct unsol_bcast_probe_resp *ubpr)
{
struct nlattr *attr;
@@ -4846,15 +4858,15 @@
wpa_printf(MSG_DEBUG,
"nl80211: Unsolicited broadcast Probe Response frame interval: %u",
- params->unsol_bcast_probe_resp_interval);
+ ubpr->unsol_bcast_probe_resp_interval);
attr = nla_nest_start(msg, NL80211_ATTR_UNSOL_BCAST_PROBE_RESP);
if (!attr ||
nla_put_u32(msg, NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT,
- params->unsol_bcast_probe_resp_interval) ||
- (params->unsol_bcast_probe_resp_tmpl &&
+ ubpr->unsol_bcast_probe_resp_interval) ||
+ (ubpr->unsol_bcast_probe_resp_tmpl &&
nla_put(msg, NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL,
- params->unsol_bcast_probe_resp_tmpl_len,
- params->unsol_bcast_probe_resp_tmpl)))
+ ubpr->unsol_bcast_probe_resp_tmpl_len,
+ ubpr->unsol_bcast_probe_resp_tmpl)))
return -1;
nla_nest_end(msg, attr);
@@ -5233,10 +5245,10 @@
params->key_mgmt_suites);
num_suites = wpa_key_mgmt_to_suites(params->key_mgmt_suites,
suites, ARRAY_SIZE(suites));
- if (num_suites > NL80211_MAX_NR_AKM_SUITES)
+ if ((unsigned int) num_suites > drv->capa.max_num_akms)
wpa_printf(MSG_DEBUG,
- "nl80211: Not enough room for all AKM suites (num_suites=%d > NL80211_MAX_NR_AKM_SUITES)",
- num_suites);
+ "nl80211: Not enough room for all AKM suites (num_suites=%d > %d)",
+ num_suites, drv->capa.max_num_akms);
else if (num_suites &&
nla_put(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32),
suites))
@@ -5439,8 +5451,8 @@
goto fail;
}
- if (params->unsol_bcast_probe_resp_interval &&
- nl80211_unsol_bcast_probe_resp(bss, msg, params) < 0)
+ if (params->ubpr.unsol_bcast_probe_resp_interval &&
+ nl80211_unsol_bcast_probe_resp(bss, msg, ¶ms->ubpr) < 0)
goto fail;
if (nl80211_mbssid(msg, params) < 0)
@@ -7812,25 +7824,37 @@
}
-static int i802_flush(void *priv)
+static int i802_flush(void *priv, int link_id)
{
struct i802_bss *bss = priv;
struct nl_msg *msg;
int res;
- wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
- bss->ifname);
+ if (link_id == NL80211_DRV_LINK_ID_NA)
+ wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
+ bss->ifname);
+ else
+ wpa_printf(MSG_DEBUG,
+ "nl80211: flush -> DEL_STATION %s (with link %d)",
+ bss->ifname, link_id);
/*
* XXX: FIX! this needs to flush all VLANs too
*/
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
+ if (link_id >= 0 && (bss->valid_links & BIT(link_id)) &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+ goto fail;
+
res = send_and_recv_cmd(bss->drv, msg);
if (res) {
wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
"(%s)", res, strerror(-res));
}
return res;
+fail:
+ nlmsg_free(msg);
+ return -1;
}
@@ -8337,7 +8361,7 @@
return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.deauth), 0, 0, 0, 0,
- 0, NULL, 0, 0, -1);
+ 0, NULL, 0, 0, link_id);
}
@@ -9965,6 +9989,9 @@
WPA_DRIVER_FLAGS2_SEC_LTF_AP;
}
+ if (os_strstr(param, "rsn_override_in_driver=1"))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_RSN_OVERRIDE_STA;
+
return 0;
}
@@ -10611,11 +10638,16 @@
nl80211_tdls_set_discovery_resp_link(drv, link_id) < 0)
return -EOPNOTSUPP;
+ if (link_id < 0 && drv->sta_mlo_info.valid_links)
+ link_id = drv->sta_mlo_info.assoc_link_id;
+
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_MGMT)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
nla_put_u8(msg, NL80211_ATTR_TDLS_ACTION, action_code) ||
nla_put_u8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token) ||
nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status_code) ||
+ (link_id >= 0 &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id)) ||
nl80211_add_peer_capab(msg, peer_capab) ||
(initiator && nla_put_flag(msg, NL80211_ATTR_TDLS_INITIATOR)) ||
nla_put(msg, NL80211_ATTR_IE, len, buf))
@@ -10818,7 +10850,7 @@
bss->ctx = bss->flink->ctx;
- if (drv->first_bss == bss && !bss->valid_links)
+ if (drv->first_bss == bss && bss->valid_links)
drv->ctx = bss->ctx;
if (!bss->valid_links) {
@@ -11386,6 +11418,13 @@
goto fail;
nla_nest_end(msg, beacon_csa);
+
+#ifdef CONFIG_IEEE80211AX
+ if (settings->ubpr.unsol_bcast_probe_resp_interval &&
+ nl80211_unsol_bcast_probe_resp(bss, msg, &settings->ubpr) < 0)
+ goto fail;
+#endif /* CONFIG_IEEE80211AX */
+
ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: switch_channel failed err=%d (%s)",
@@ -11467,6 +11506,27 @@
}
nla_nest_end(msg, beacon_cca);
+
+ if (settings->ubpr.unsol_bcast_probe_resp_interval &&
+ nl80211_unsol_bcast_probe_resp(bss, msg, &settings->ubpr) < 0) {
+ ret = -ENOBUFS;
+ goto error;
+ }
+
+#ifdef CONFIG_IEEE80211BE
+ if (nl80211_link_valid(bss->valid_links, settings->link_id)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Color change request on link_id=%d",
+ settings->link_id);
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
+ settings->link_id)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
ret = send_and_recv_cmd(drv, msg);
if (ret) {
wpa_printf(MSG_DEBUG,
@@ -14005,7 +14065,7 @@
}
}
- msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ADD_LINK);
+ msg = nl80211_bss_msg(bss, 0, NL80211_CMD_ADD_LINK);
if (!msg ||
nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
@@ -14029,8 +14089,8 @@
bss->valid_links |= BIT(link_id);
bss->links[link_id].ctx = bss_ctx;
- wpa_printf(MSG_DEBUG, "nl80211: MLD: valid_links=0x%04x",
- bss->valid_links);
+ wpa_printf(MSG_DEBUG, "nl80211: MLD: valid_links=0x%04x on %s",
+ bss->valid_links, bss->ifname);
return 0;
}
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index d8375cc..ebf69dc 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -1450,6 +1450,14 @@
if (check_feature(QCA_WLAN_VENDOR_FEATURE_AP_ALLOWED_FREQ_LIST,
&info))
drv->qca_ap_allowed_freqs = 1;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_HT_VHT_TWT_RESPONDER, &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_HT_VHT_TWT_RESPONDER;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_RSN_OVERRIDE_STA, &info)) {
+ wpa_printf(MSG_DEBUG,
+ "The driver supports RSN overriding in STA mode");
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_RSN_OVERRIDE_STA;
+ }
+
os_free(info.flags);
}
@@ -1477,6 +1485,7 @@
WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256 |
WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
WPA_DRIVER_CAPA_KEY_MGMT_OWE |
WPA_DRIVER_CAPA_KEY_MGMT_DPP;
@@ -1492,6 +1501,7 @@
WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 |
+ WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY |
WPA_DRIVER_CAPA_KEY_MGMT_SAE;
else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
drv->capa.key_mgmt |=
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 9ce73c6..d381440 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -21,7 +21,7 @@
#include "driver_nl80211.h"
static void
-nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
+nl80211_control_port_frame_tx_status(struct i802_bss *bss,
const u8 *frame, size_t len,
struct nlattr *ack, struct nlattr *cookie);
@@ -1272,8 +1272,14 @@
mld_link = nl80211_get_link(bss,
data.ch_switch.link_id);
mld_link->freq = data.ch_switch.freq;
+ if (bw)
+ mld_link->bandwidth = channel_width_to_int(
+ data.ch_switch.ch_width);
} else {
bss->flink->freq = data.ch_switch.freq;
+ if (bw)
+ bss->flink->bandwidth = channel_width_to_int(
+ data.ch_switch.ch_width);
}
}
@@ -1367,18 +1373,20 @@
event.rx_mgmt.frame_len = len;
event.rx_mgmt.ssi_signal = ssi_signal;
event.rx_mgmt.drv_priv = bss;
+ event.rx_mgmt.ctx = bss->ctx;
event.rx_mgmt.link_id = link_id;
wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
}
-static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
+static void mlme_event_mgmt_tx_status(struct i802_bss *bss,
struct nlattr *cookie, const u8 *frame,
size_t len, struct nlattr *ack)
{
union wpa_event_data event;
const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
u16 fc = le_to_host16(hdr->frame_control);
u64 cookie_val = 0;
@@ -1397,7 +1405,7 @@
WPA_GET_BE16(frame + 2 * ETH_ALEN) == ETH_P_PAE) {
wpa_printf(MSG_DEBUG,
"nl80211: Work around misdelivered control port TX status for EAPOL");
- nl80211_control_port_frame_tx_status(drv, frame, len, ack,
+ nl80211_control_port_frame_tx_status(bss, frame, len, ack,
cookie);
return;
}
@@ -1433,7 +1441,7 @@
event.tx_status.ack = ack != NULL;
event.tx_status.link_id = cookie_val == drv->send_frame_cookie ?
drv->send_frame_link_id : NL80211_DRV_LINK_ID_NA;
- wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
+ wpa_supplicant_event(bss->ctx, EVENT_TX_STATUS, &event);
}
@@ -1741,7 +1749,7 @@
nla_len(frame), link_id);
break;
case NL80211_CMD_FRAME_TX_STATUS:
- mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
+ mlme_event_mgmt_tx_status(bss, cookie, nla_data(frame),
nla_len(frame), ack);
break;
case NL80211_CMD_UNPROT_DEAUTHENTICATE:
@@ -3657,8 +3665,7 @@
}
-static void nl80211_control_port_frame(struct wpa_driver_nl80211_data *drv,
- struct nlattr **tb)
+static void nl80211_control_port_frame(struct i802_bss *bss, struct nlattr **tb)
{
u8 *src_addr;
u16 ethertype;
@@ -3687,7 +3694,7 @@
MAC2STR(src_addr));
break;
case ETH_P_PAE:
- drv_event_eapol_rx2(drv->ctx, src_addr,
+ drv_event_eapol_rx2(bss->ctx, src_addr,
nla_data(tb[NL80211_ATTR_FRAME]),
nla_len(tb[NL80211_ATTR_FRAME]),
encrypted, link_id);
@@ -3703,10 +3710,11 @@
static void
-nl80211_control_port_frame_tx_status(struct wpa_driver_nl80211_data *drv,
+nl80211_control_port_frame_tx_status(struct i802_bss *bss,
const u8 *frame, size_t len,
struct nlattr *ack, struct nlattr *cookie)
{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
union wpa_event_data event;
if (!cookie || len < ETH_HLEN)
@@ -3725,7 +3733,7 @@
nla_get_u64(cookie) == drv->eapol_tx_cookie ?
drv->eapol_tx_link_id : NL80211_DRV_LINK_ID_NA;
- wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
+ wpa_supplicant_event(bss->ctx, EVENT_EAPOL_TX_STATUS, &event);
}
@@ -3783,48 +3791,62 @@
#ifdef CONFIG_IEEE80211AX
-static void nl80211_obss_color_collision(struct i802_bss *bss,
- struct nlattr *tb[])
+static void nl80211_obss_color_event(struct i802_bss *bss,
+ enum nl80211_commands cmd,
+ struct nlattr *tb[])
{
union wpa_event_data data;
-
- if (!tb[NL80211_ATTR_OBSS_COLOR_BITMAP])
- return;
+ enum wpa_event_type event_type;
os_memset(&data, 0, sizeof(data));
- data.bss_color_collision.bitmap =
- nla_get_u64(tb[NL80211_ATTR_OBSS_COLOR_BITMAP]);
+ data.bss_color_collision.link_id = NL80211_DRV_LINK_ID_NA;
- wpa_printf(MSG_DEBUG, "nl80211: BSS color collision - bitmap %08llx",
- (long long unsigned int) data.bss_color_collision.bitmap);
- wpa_supplicant_event(bss->ctx, EVENT_BSS_COLOR_COLLISION, &data);
-}
+ switch (cmd) {
+ case NL80211_CMD_OBSS_COLOR_COLLISION:
+ event_type = EVENT_BSS_COLOR_COLLISION;
+ if (!tb[NL80211_ATTR_OBSS_COLOR_BITMAP])
+ return;
+ data.bss_color_collision.bitmap =
+ nla_get_u64(tb[NL80211_ATTR_OBSS_COLOR_BITMAP]);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: BSS color collision - bitmap %08llx",
+ (long long unsigned int)
+ data.bss_color_collision.bitmap);
+ break;
+ case NL80211_CMD_COLOR_CHANGE_STARTED:
+ event_type = EVENT_CCA_STARTED_NOTIFY;
+ wpa_printf(MSG_DEBUG, "nl80211: CCA started");
+ break;
+ case NL80211_CMD_COLOR_CHANGE_ABORTED:
+ event_type = EVENT_CCA_ABORTED_NOTIFY;
+ wpa_printf(MSG_DEBUG, "nl80211: CCA aborted");
+ break;
+ case NL80211_CMD_COLOR_CHANGE_COMPLETED:
+ event_type = EVENT_CCA_NOTIFY;
+ wpa_printf(MSG_DEBUG, "nl80211: CCA completed");
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "nl80211: Unknown CCA command %d", cmd);
+ return;
+ }
+ if (tb[NL80211_ATTR_MLO_LINK_ID]) {
+ data.bss_color_collision.link_id =
+ nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
-static void nl80211_color_change_announcement_started(struct i802_bss *bss)
-{
- union wpa_event_data data = {};
+ if (!nl80211_link_valid(bss->valid_links,
+ data.bss_color_collision.link_id)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Invalid BSS color event link ID %d",
+ data.bss_color_collision.link_id);
+ return;
+ }
- wpa_printf(MSG_DEBUG, "nl80211: CCA started");
- wpa_supplicant_event(bss->ctx, EVENT_CCA_STARTED_NOTIFY, &data);
-}
+ wpa_printf(MSG_DEBUG, "nl80211: BSS color event - Link ID %d",
+ data.bss_color_collision.link_id);
+ }
-
-static void nl80211_color_change_announcement_aborted(struct i802_bss *bss)
-{
- union wpa_event_data data = {};
-
- wpa_printf(MSG_DEBUG, "nl80211: CCA aborted");
- wpa_supplicant_event(bss->ctx, EVENT_CCA_ABORTED_NOTIFY, &data);
-}
-
-
-static void nl80211_color_change_announcement_completed(struct i802_bss *bss)
-{
- union wpa_event_data data = {};
-
- wpa_printf(MSG_DEBUG, "nl80211: CCA completed");
- wpa_supplicant_event(bss->ctx, EVENT_CCA_NOTIFY, &data);
+ wpa_supplicant_event(bss->ctx, event_type, &data);
}
#endif /* CONFIG_IEEE80211AX */
@@ -4070,7 +4092,7 @@
case NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS:
if (!frame)
break;
- nl80211_control_port_frame_tx_status(drv,
+ nl80211_control_port_frame_tx_status(bss,
nla_data(frame),
nla_len(frame),
tb[NL80211_ATTR_ACK],
@@ -4085,16 +4107,10 @@
break;
#ifdef CONFIG_IEEE80211AX
case NL80211_CMD_OBSS_COLOR_COLLISION:
- nl80211_obss_color_collision(bss, tb);
- break;
case NL80211_CMD_COLOR_CHANGE_STARTED:
- nl80211_color_change_announcement_started(bss);
- break;
case NL80211_CMD_COLOR_CHANGE_ABORTED:
- nl80211_color_change_announcement_aborted(bss);
- break;
case NL80211_CMD_COLOR_CHANGE_COMPLETED:
- nl80211_color_change_announcement_completed(bss);
+ nl80211_obss_color_event(bss, cmd, tb);
break;
#endif /* CONFIG_IEEE80211AX */
case NL80211_CMD_LINKS_REMOVED:
@@ -4243,7 +4259,7 @@
nl80211_external_auth(bss->drv, tb);
break;
case NL80211_CMD_CONTROL_PORT_FRAME:
- nl80211_control_port_frame(bss->drv, tb);
+ nl80211_control_port_frame(bss, tb);
break;
default:
wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index 1eb4374..b055e68 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -402,23 +402,18 @@
wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
"(%s)", ret, strerror(-ret));
if (drv->hostapd && is_ap_interface(drv->nlmode)) {
- enum nl80211_iftype old_mode = drv->nlmode;
-
/*
* mac80211 does not allow scan requests in AP mode, so
* try to do this in station mode.
*/
+ drv->ap_scan_as_station = drv->nlmode;
if (wpa_driver_nl80211_set_mode(
- bss, NL80211_IFTYPE_STATION))
- goto fail;
-
- if (wpa_driver_nl80211_scan(bss, params)) {
- wpa_driver_nl80211_set_mode(bss, old_mode);
+ bss, NL80211_IFTYPE_STATION) ||
+ wpa_driver_nl80211_scan(bss, params)) {
+ nl80211_restore_ap_mode(bss);
goto fail;
}
- /* Restore AP mode when processing scan results */
- drv->ap_scan_as_station = old_mode;
ret = 0;
} else
goto fail;
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index 3b3098d..0186099 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -160,6 +160,7 @@
NEED_LINUX_IOCTL=y
ifdef CONFIG_VLAN_NETLINK
NEED_LIBNL=y
+CONFIG_LIBNL3_ROUTE=y
endif
endif
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 1cbe652..8c58456 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -154,6 +154,7 @@
NEED_LINUX_IOCTL=y
ifdef CONFIG_VLAN_NETLINK
NEED_LIBNL=y
+CONFIG_LIBNL3_ROUTE=y
endif
endif
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 0f9c924..df7cb7f 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -3348,7 +3348,7 @@
}
-static void p2p_sd_cb(struct p2p_data *p2p, int success)
+void p2p_sd_query_cb(struct p2p_data *p2p, int success)
{
p2p_dbg(p2p, "Service Discovery Query TX callback: success=%d",
success);
@@ -3851,7 +3851,7 @@
p2p_go_neg_conf_cb(p2p, result);
break;
case P2P_PENDING_SD:
- p2p_sd_cb(p2p, success);
+ p2p_sd_query_cb(p2p, success);
break;
case P2P_PENDING_PD:
p2p_prov_disc_cb(p2p, success);
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index f24544c..5239ee4 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -910,6 +910,9 @@
const struct weighted_pcl *freq_list,
unsigned int num_channels,
struct p2p_channels *res, bool go);
+
+void p2p_sd_query_cb(struct p2p_data *p2p, int success);
+
void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
PRINTF_FORMAT(2, 3);
void p2p_info(struct p2p_data *p2p, const char *fmt, ...)
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index d6882e4..5537a6e 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -502,7 +502,7 @@
* received. */
p2p_dbg(p2p,
"GAS Initial Request had not yet received TX status - process the response anyway");
- p2p_set_state(p2p, P2P_SD_DURING_FIND);
+ p2p_sd_query_cb(p2p, 1);
}
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p_clear_timeout(p2p);
diff --git a/src/radius/radius.c b/src/radius/radius.c
index be59a94..37aa216 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -423,25 +423,54 @@
}
+u8 * radius_msg_add_msg_auth(struct radius_msg *msg)
+{
+ u8 auth[MD5_MAC_LEN];
+ struct radius_attr_hdr *attr;
+
+ os_memset(auth, 0, MD5_MAC_LEN);
+ attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+ auth, MD5_MAC_LEN);
+ if (!attr) {
+ wpa_printf(MSG_ERROR,
+ "WARNING: Could not add Message-Authenticator");
+ return NULL;
+ }
+
+ return (u8 *) (attr + 1);
+}
+
+
+static u8 * radius_msg_auth_pos(struct radius_msg *msg)
+{
+ u8 *pos;
+ size_t alen;
+
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+ &pos, &alen, NULL) == 0 &&
+ alen == MD5_MAC_LEN) {
+ /* Use already added Message-Authenticator attribute */
+ return pos;
+ }
+
+ /* Add a Message-Authenticator attribute */
+ return radius_msg_add_msg_auth(msg);
+}
+
+
int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
size_t secret_len)
{
if (secret) {
- u8 auth[MD5_MAC_LEN];
- struct radius_attr_hdr *attr;
+ u8 *pos;
- os_memset(auth, 0, MD5_MAC_LEN);
- attr = radius_msg_add_attr(msg,
- RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
- auth, MD5_MAC_LEN);
- if (attr == NULL) {
- wpa_printf(MSG_WARNING, "RADIUS: Could not add "
- "Message-Authenticator");
+ pos = radius_msg_auth_pos(msg);
+ if (!pos)
return -1;
- }
msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
- hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
- wpabuf_len(msg->buf), (u8 *) (attr + 1));
+ if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+ wpabuf_len(msg->buf), pos) < 0)
+ return -1;
} else
msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
@@ -457,23 +486,19 @@
int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
size_t secret_len, const u8 *req_authenticator)
{
- u8 auth[MD5_MAC_LEN];
- struct radius_attr_hdr *attr;
const u8 *addr[4];
size_t len[4];
+ u8 *pos;
- os_memset(auth, 0, MD5_MAC_LEN);
- attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
- auth, MD5_MAC_LEN);
- if (attr == NULL) {
- wpa_printf(MSG_ERROR, "WARNING: Could not add Message-Authenticator");
+ pos = radius_msg_auth_pos(msg);
+ if (!pos)
return -1;
- }
msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
os_memcpy(msg->hdr->authenticator, req_authenticator,
sizeof(msg->hdr->authenticator));
- hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
- wpabuf_len(msg->buf), (u8 *) (attr + 1));
+ if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+ wpabuf_len(msg->buf), pos) < 0)
+ return -1;
/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
addr[0] = (u8 *) msg->hdr;
@@ -501,21 +526,17 @@
{
const u8 *addr[2];
size_t len[2];
- u8 auth[MD5_MAC_LEN];
- struct radius_attr_hdr *attr;
+ u8 *pos;
- os_memset(auth, 0, MD5_MAC_LEN);
- attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
- auth, MD5_MAC_LEN);
- if (attr == NULL) {
- wpa_printf(MSG_WARNING, "Could not add Message-Authenticator");
+ pos = radius_msg_auth_pos(msg);
+ if (!pos)
return -1;
- }
msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16);
- hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
- wpabuf_len(msg->buf), (u8 *) (attr + 1));
+ if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+ wpabuf_len(msg->buf), pos) < 0)
+ return -1;
/* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
addr[0] = wpabuf_head_u8(msg->buf);
@@ -980,6 +1001,20 @@
return 1;
}
+ if (!auth) {
+ u8 *pos;
+ size_t alen;
+
+ if (radius_msg_get_attr_ptr(msg,
+ RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+ &pos, &alen, NULL) == 0) {
+ /* Check the Message-Authenticator attribute since it
+ * was included even if we are configured to not
+ * require it. */
+ auth = 1;
+ }
+ }
+
if (auth &&
radius_msg_verify_msg_auth(msg, secret, secret_len,
sent_msg->hdr->authenticator)) {
diff --git a/src/radius/radius.h b/src/radius/radius.h
index 571c159..05fddba 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -268,6 +268,7 @@
struct radius_msg * radius_msg_new(u8 code, u8 identifier);
void radius_msg_free(struct radius_msg *msg);
void radius_msg_dump(struct radius_msg *msg);
+u8 * radius_msg_add_msg_auth(struct radius_msg *msg);
int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
size_t secret_len);
int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
index aaa3fc2..8d7c9b4 100644
--- a/src/radius/radius_das.c
+++ b/src/radius/radius_das.c
@@ -177,6 +177,11 @@
if (reply == NULL)
return NULL;
+ if (!radius_msg_add_msg_auth(reply)) {
+ radius_msg_free(reply);
+ return NULL;
+ }
+
if (error) {
if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
error)) {
@@ -368,6 +373,11 @@
if (!reply)
return NULL;
+ if (!radius_msg_add_msg_auth(reply)) {
+ radius_msg_free(reply);
+ return NULL;
+ }
+
if (error &&
!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, error)) {
radius_msg_free(reply);
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index e02c215..fa36915 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -920,6 +920,11 @@
return NULL;
}
+ if (!radius_msg_add_msg_auth(msg)) {
+ radius_msg_free(msg);
+ return NULL;
+ }
+
sess_id = htonl(sess->sess_id);
if (code == RADIUS_CODE_ACCESS_CHALLENGE &&
!radius_msg_add_attr(msg, RADIUS_ATTR_STATE,
@@ -1204,6 +1209,11 @@
return NULL;
}
+ if (!radius_msg_add_msg_auth(msg)) {
+ radius_msg_free(msg);
+ return NULL;
+ }
+
if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
radius_msg_free(msg);
@@ -1253,6 +1263,11 @@
return -1;
}
+ if (!radius_msg_add_msg_auth(msg)) {
+ radius_msg_free(msg);
+ return -1;
+ }
+
os_memset(&eapfail, 0, sizeof(eapfail));
eapfail.code = EAP_CODE_FAILURE;
eapfail.identifier = 0;
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index a402cb6..cb7f7e4 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -255,13 +255,13 @@
if (pmkid) {
os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
} else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
- if (kck) {
+ if (kck && kck_len > 0) {
rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
os_memcpy(entry->kck, kck, kck_len);
entry->kck_len = kck_len;
}
} else if (wpa_key_mgmt_suite_b(akmp)) {
- if (kck) {
+ if (kck && kck_len > 0) {
rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
os_memcpy(entry->kck, kck, kck_len);
entry->kck_len = kck_len;
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index f5e24f2..935a1aa 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -1399,6 +1399,7 @@
const u8 *key_rsc, int wnm_sleep)
{
const u8 *gtk = gd->gtk;
+ u8 gtk_buf[32];
/* Detect possible key reinstallation */
if ((sm->mlo.links[link_id].gtk.gtk_len == (size_t) gd->gtk_len &&
@@ -1421,14 +1422,23 @@
link_id, gd->keyidx, gd->tx, gd->gtk_len);
wpa_hexdump_link(MSG_DEBUG, link_id, "RSN: RSC",
key_rsc, gd->key_rsc_len);
+ if (sm->group_cipher == WPA_CIPHER_TKIP) {
+ /* Swap Tx/Rx keys for Michael MIC */
+ os_memcpy(gtk_buf, gd->gtk, 16);
+ os_memcpy(gtk_buf + 16, gd->gtk + 24, 8);
+ os_memcpy(gtk_buf + 24, gd->gtk + 16, 8);
+ gtk = gtk_buf;
+ }
if (wpa_sm_set_key(sm, link_id, gd->alg, broadcast_ether_addr,
gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, gtk,
gd->gtk_len, KEY_FLAG_GROUP_RX) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"RSN: Failed to set GTK to the driver (link_id=%d alg=%d keylen=%d keyidx=%d)",
link_id, gd->alg, gd->gtk_len, gd->keyidx);
+ forced_memzero(gtk_buf, sizeof(gtk_buf));
return -1;
}
+ forced_memzero(gtk_buf, sizeof(gtk_buf));
if (wnm_sleep) {
sm->mlo.links[link_id].gtk_wnm_sleep.gtk_len = gd->gtk_len;
@@ -2543,6 +2553,27 @@
if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
goto failed;
+ if (sm->ssid_protection) {
+ if (!ie.ssid) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: No SSID included in EAPOL-Key msg 3/4");
+ goto failed;
+ }
+
+ if (ie.ssid_len != sm->ssid_len ||
+ os_memcmp(ie.ssid, sm->ssid, sm->ssid_len) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: SSID mismatch in EAPOL-Key msg 3/4");
+ wpa_hexdump_ascii(MSG_DEBUG, "RSN: Received SSID",
+ ie.ssid, ie.ssid_len);
+ wpa_hexdump_ascii(MSG_DEBUG, "RSN: Expected SSID",
+ sm->ssid, sm->ssid_len);
+ goto failed;
+ }
+
+ wpa_sm_ssid_verified(sm);
+ }
+
if (mlo && !ie.valid_mlo_gtks) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"MLO RSN: No GTK KDE included in EAPOL-Key msg 3/4");
@@ -4461,6 +4492,20 @@
}
+void wpa_sm_set_ssid(struct wpa_sm *sm, const u8 *ssid, size_t ssid_len)
+{
+ if (!sm)
+ return;
+
+ if (ssid) {
+ os_memcpy(sm->ssid, ssid, ssid_len);
+ sm->ssid_len = ssid_len;
+ } else {
+ sm->ssid_len = 0;
+ }
+}
+
+
int wpa_sm_set_mlo_params(struct wpa_sm *sm, const struct wpa_sm_mlo *mlo)
{
int i;
@@ -4506,12 +4551,27 @@
} else {
wpa_hexdump_link(MSG_DEBUG, i, "RSN: Set AP RSNE",
ie, len);
- sm->mlo.links[i].ap_rsne = os_memdup(ie, len);
- if (!sm->mlo.links[i].ap_rsne) {
- sm->mlo.links[i].ap_rsne_len = 0;
- return -1;
+ if (ie[0] == WLAN_EID_VENDOR_SPECIFIC && len > 2 + 4) {
+ sm->mlo.links[i].ap_rsne = os_malloc(len - 4);
+ if (!sm->mlo.links[i].ap_rsne)
+ return -1;
+ sm->mlo.links[i].ap_rsne[0] = WLAN_EID_RSN;
+ sm->mlo.links[i].ap_rsne[1] = len - 2 - 4;
+ os_memcpy(&sm->mlo.links[i].ap_rsne[2],
+ ie + 2 + 4, len - 2 - 4);
+ sm->mlo.links[i].ap_rsne_len = len - 4;
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Converted RSNE override to RSNE",
+ sm->mlo.links[i].ap_rsne,
+ sm->mlo.links[i].ap_rsne_len);
+ } else {
+ sm->mlo.links[i].ap_rsne = os_memdup(ie, len);
+ if (!sm->mlo.links[i].ap_rsne) {
+ sm->mlo.links[i].ap_rsne_len = 0;
+ return -1;
+ }
+ sm->mlo.links[i].ap_rsne_len = len;
}
- sm->mlo.links[i].ap_rsne_len = len;
}
ie = mlo->links[i].ap_rsnxe;
@@ -4527,12 +4587,27 @@
} else {
wpa_hexdump_link(MSG_DEBUG, i, "RSN: Set AP RSNXE", ie,
len);
- sm->mlo.links[i].ap_rsnxe = os_memdup(ie, len);
- if (!sm->mlo.links[i].ap_rsnxe) {
- sm->mlo.links[i].ap_rsnxe_len = 0;
- return -1;
+ if (ie[0] == WLAN_EID_VENDOR_SPECIFIC && len > 2 + 4) {
+ sm->mlo.links[i].ap_rsnxe = os_malloc(len - 4);
+ if (!sm->mlo.links[i].ap_rsnxe)
+ return -1;
+ sm->mlo.links[i].ap_rsnxe[0] = WLAN_EID_RSNX;
+ sm->mlo.links[i].ap_rsnxe[1] = len - 2 - 4;
+ os_memcpy(&sm->mlo.links[i].ap_rsnxe[2],
+ ie + 2 + 4, len - 2 - 4);
+ sm->mlo.links[i].ap_rsnxe_len = len - 4;
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Converted RSNXE override to RSNXE",
+ sm->mlo.links[i].ap_rsnxe,
+ sm->mlo.links[i].ap_rsnxe_len);
+ } else {
+ sm->mlo.links[i].ap_rsnxe = os_memdup(ie, len);
+ if (!sm->mlo.links[i].ap_rsnxe) {
+ sm->mlo.links[i].ap_rsnxe_len = 0;
+ return -1;
+ }
+ sm->mlo.links[i].ap_rsnxe_len = len;
}
- sm->mlo.links[i].ap_rsnxe_len = len;
}
}
@@ -4690,6 +4765,9 @@
case WPA_PARAM_FT_PREPEND_PMKID:
sm->ft_prepend_pmkid = value;
break;
+ case WPA_PARAM_SSID_PROTECTION:
+ sm->ssid_protection = value;
+ break;
default:
break;
}
@@ -4961,6 +5039,14 @@
sm->assoc_rsnxe_len = len;
}
+ if (sm->ssid_protection &&
+ !ieee802_11_rsnx_capab(sm->assoc_rsnxe,
+ WLAN_RSNX_CAPAB_SSID_PROTECTION)) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "RSN: Disabling SSID protection based on own RSNXE update");
+ sm->ssid_protection = 0;
+ }
+
return 0;
}
@@ -5022,11 +5108,24 @@
sm->ap_rsn_ie_len = 0;
} else {
wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len);
- sm->ap_rsn_ie = os_memdup(ie, len);
- if (sm->ap_rsn_ie == NULL)
- return -1;
+ if (ie[0] == WLAN_EID_VENDOR_SPECIFIC && len > 2 + 4) {
+ sm->ap_rsn_ie = os_malloc(len - 4);
+ if (!sm->ap_rsn_ie)
+ return -1;
+ sm->ap_rsn_ie[0] = WLAN_EID_RSN;
+ sm->ap_rsn_ie[1] = len - 2 - 4;
+ os_memcpy(&sm->ap_rsn_ie[2], ie + 2 + 4, len - 2 - 4);
+ sm->ap_rsn_ie_len = len - 4;
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Converted RSNE override to RSNE",
+ sm->ap_rsn_ie, sm->ap_rsn_ie_len);
+ } else {
+ sm->ap_rsn_ie = os_memdup(ie, len);
+ if (sm->ap_rsn_ie == NULL)
+ return -1;
- sm->ap_rsn_ie_len = len;
+ sm->ap_rsn_ie_len = len;
+ }
}
return 0;
@@ -5055,11 +5154,24 @@
sm->ap_rsnxe_len = 0;
} else {
wpa_hexdump(MSG_DEBUG, "WPA: set AP RSNXE", ie, len);
- sm->ap_rsnxe = os_memdup(ie, len);
- if (!sm->ap_rsnxe)
- return -1;
+ if (ie[0] == WLAN_EID_VENDOR_SPECIFIC && len > 2 + 4) {
+ sm->ap_rsnxe = os_malloc(len - 4);
+ if (!sm->ap_rsnxe)
+ return -1;
+ sm->ap_rsnxe[0] = WLAN_EID_RSNX;
+ sm->ap_rsnxe[1] = len - 2 - 4;
+ os_memcpy(&sm->ap_rsnxe[2], ie + 2 + 4, len - 2 - 4);
+ sm->ap_rsnxe_len = len - 4;
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Converted RSNXE override to RSNXE",
+ sm->ap_rsnxe, sm->ap_rsnxe_len);
+ } else {
+ sm->ap_rsnxe = os_memdup(ie, len);
+ if (!sm->ap_rsnxe)
+ return -1;
- sm->ap_rsnxe_len = len;
+ sm->ap_rsnxe_len = len;
+ }
}
return 0;
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 7d790a6..231e088 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -104,6 +104,7 @@
#endif /* CONFIG_PASN */
void (*notify_pmksa_cache_entry)(void *ctx,
struct rsn_pmksa_cache_entry *entry);
+ void (*ssid_verified)(void *ctx);
};
@@ -135,6 +136,7 @@
WPA_PARAM_ENCRYPT_EAPOL_M2,
WPA_PARAM_ENCRYPT_EAPOL_M4,
WPA_PARAM_FT_PREPEND_PMKID,
+ WPA_PARAM_SSID_PROTECTION,
};
struct rsn_supp_config {
@@ -188,6 +190,7 @@
void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth);
void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx);
void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config);
+void wpa_sm_set_ssid(struct wpa_sm *sm, const u8 *ssid, size_t ssid_len);
void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr);
void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
const char *bridge_ifname);
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index ca97c12..d7e7805 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -112,6 +112,7 @@
unsigned int secure_ltf:1;
unsigned int secure_rtt:1;
unsigned int prot_range_neg:1;
+ unsigned int ssid_protection:1;
u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
size_t assoc_wpa_ie_len;
@@ -517,6 +518,12 @@
sm->ctx->notify_pmksa_cache_entry(sm->ctx->ctx, entry);
}
+static inline void wpa_sm_ssid_verified(struct wpa_sm *sm)
+{
+ if (sm->ctx->ssid_verified)
+ sm->ctx->ssid_verified(sm->ctx->ctx);
+}
+
int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
int ver, const u8 *dest, u16 proto,
u8 *msg, size_t msg_len, u8 *key_mic);
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index d1510aa..515f1b0 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -33,8 +33,15 @@
if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC &&
wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE)
return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
- else
- return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data);
+ if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC &&
+ wpa_ie[1] >= 4 &&
+ WPA_GET_BE32(&wpa_ie[2]) == RSNE_OVERRIDE_IE_VENDOR_TYPE)
+ return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
+ if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC &&
+ wpa_ie[1] >= 4 &&
+ WPA_GET_BE32(&wpa_ie[2]) == RSNE_OVERRIDE_2_IE_VENDOR_TYPE)
+ return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
+ return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data);
}
@@ -366,7 +373,7 @@
int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len)
{
u8 *pos = rsnxe;
- u16 capab = 0;
+ u32 capab = 0, tmp;
size_t flen;
if (wpa_key_mgmt_sae(sm->key_mgmt) &&
@@ -385,20 +392,27 @@
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
if (sm->prot_range_neg)
capab |= BIT(WLAN_RSNX_CAPAB_URNM_MFPR);
+ if (sm->ssid_protection)
+ capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
- flen = (capab & 0xff00) ? 2 : 1;
if (!capab)
return 0; /* no supported extended RSN capabilities */
+ tmp = capab;
+ flen = 0;
+ while (tmp) {
+ flen++;
+ tmp >>= 8;
+ }
if (rsnxe_len < 2 + flen)
return -1;
capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
*pos++ = WLAN_EID_RSNX;
*pos++ = flen;
- *pos++ = capab & 0x00ff;
- capab >>= 8;
- if (capab)
- *pos++ = capab;
+ while (capab) {
+ *pos++ = capab & 0xff;
+ capab >>= 8;
+ }
return pos - rsnxe;
}
diff --git a/src/utils/common.c b/src/utils/common.c
index 6acfcbd..d62dec7 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -990,6 +990,19 @@
}
+bool int_array_includes(int *arr, int val)
+{
+ int i;
+
+ for (i = 0; arr && arr[i]; i++) {
+ if (val == arr[i])
+ return true;
+ }
+
+ return false;
+}
+
+
void str_clear_free(char *str)
{
if (str) {
diff --git a/src/utils/common.h b/src/utils/common.h
index 14c90c9..aed93fb 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -578,6 +578,7 @@
void int_array_concat(int **res, const int *a);
void int_array_sort_unique(int *a);
void int_array_add_unique(int **res, int a);
+bool int_array_includes(int *arr, int val);
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index e5b656a..e190645 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -624,6 +624,7 @@
while (i < res) {
int allow_skip = 1;
int maybe = 0;
+ bool prefix = false;
if (*pos == '=') {
allow_skip = 0;
@@ -637,7 +638,12 @@
len = next - pos;
else
len = os_strlen(pos);
- if (os_memcmp(pos, func[i], len) != 0) {
+ if (len >= 1 && pos[len - 1] == '*') {
+ prefix = true;
+ len -= 1;
+ }
+ if (os_strncmp(pos, func[i], len) != 0 ||
+ (!prefix && func[i][len] != '\0')) {
if (maybe && next) {
pos = next + 1;
continue;