[wpa_supplicant] cumilative patch from commit 3a5d1a7e6
Bug: 329004037
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/329003970)
BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from open source
3a5d1a7e6 NAN: USD in hostapd
e3f9ab3c3 NAN: USD in wpa_supplicant
9eb0bc1f0 NAN: Unsynchronized service discovery (USD)
f2ea8791c NAN: Protocol definitions
4f557c594 Add os_reltime helpers to work with milliseconds
0b5d370c0 DPP: Fix DPP Action frame check for EVENT_RX_MGMT events
8fa52a797 FT: Allow wpa_supplicant to be configured to prepend PMKR1Name
9929426b9 FT: Allow PMKIDs from AssocReq to be in EAPOL-Key msg 2/4
560389997 AP MLD: Handle EAPOL only on the association link
7ba039ba1 AP MLD: Do not allow disabling first interface affiliated with an AP MLD
9a47ede87 AP MLD: Add support for hostapd_cli to disable/enable AP MLD
0102c5c60 hostapd: Do not use prefix matching for ENABLE/RELOAD/DISABLE
03e89de47 AP MLD: Process link info when handling new STA event with driver SME
d3d59967a Handle both HT40+ and HT40- allowed consistently in channel check
e650fa4d7 ACS: Handle ACS channel selected event in specified link
0e91a86ec ACS: Add link id if operating as an AP MLD
f972420e8 AP MLD: Fix AID allocation for legacy STA
fe36750b3 Add QCA vendor command to disassociate with peer
9fe2970ff OpenSSL: Use library functions for HPKE when possible
14c5f401f Remove forgotted STAKey related functionality in EAPOL-Key Request
3f60fcdd8 FILS: Fix EAPOL-Key request generation
b27086e6e Discard EAPOL-Key request without Secure=1
096794088 Discard EAPOL-Key Request frames during 4-way handshake
8037c1ad6 Move Key Replay Counter checks for EAPOL-Key frames to helper functions
2c6147404 Check Key Descriptor Version value earlier in the process
bd1e07899 Reject undefined Key Descriptor Version values explicitly
fff69bba1 Use more generic checks for Key Descriptor Version 2 and 3
74a25a660 Remove always true check on EAPOL-Key message in authenticator
9e9afd956 Extend frequency configuration to handle 6 GHz channel 2
8677844db Add a QCA vendor attribute to determine QCA device
576f46250 P2P: Accept P2P SD response without TX status
16a22ef34 nl80211: Increase the hard scan timeout for initial attempt
f20ca22dc DFS: Print the random channel list entry selection in debug print
d88fe8fe5 DFS: Fix a typo in a debug message
e3fb9e6f2 Send actual BTM capability when the driver takes care of BSS selection
6c334d9f0 nl80211: Set allowed frequency list per link for AP MLD
42cd2376f Enhance QCA vendor interface with new SAR version numbers
5ae8838a0 Make test code easier for static analyzers
c03377cf2 SAE: Fix resource leak on reading a separate password file
348c047af ACS: More consistent checking of the best channel pointer
5d54bf6fb Fix error path on Key Data field decryption
a4d599a53 FT: Fix architecture for RxKH loading from a file
0b95d1346 OpenSSL: Fix a memory leak on an error path
456bfec47 Avoid uninitialized seq number in debug print for testing functionality
c17900278 tests: Fix a memory leak in a module test
7184e63ce dbus: Avoid memory leak on error when signaling PropertiesChanged
4037c0ac1 nl80211: Fix wiphy event handling when the driver is deinitialized
1c90c8d24 DPP: Avoid a potential use-after-free on an error path in AP
32940c7a4 DPP: Fix use-after-free in connection status reporting when using TCP
9456adeeb DPP3: Fix potential use-after-free on push button bootstrap info
e3d6fce84 EAP-SIM/AKA peer: Fix use-after-free for privacy identity
fd71cae6c nl80211: Fix memory leak on libnl nl_cb
2814dbd6d OpenSSL: Fix a memory leak in crypto_ec_key_parse_priv()
aa1aa289c AP MLD: Optimize struct mld_link_info size
93eab9f0f Mark hostapd_gen_probe_resp() static
512b92524 AP MLD: Reduce struct mld_link_info size
b91572b30 AP MLD: Fix RADIUS deinit
fa79e46c7 AP MLD: Use a helper function to set whether a STA is a non-AP MLD
6bda0aca8 AP MLD: Use a helper function to check if a STA is a non-AP MLD
ee9375fb3 tests: Association comeback mechanism in wpa_supplicant
33179cd29 SME: Handle PMF association comeback when not handled in driver
0ef4b1e1d D-Bus: Add a signal for HS2.0 terms and conditions
4e3f6b847 wlantest: Add test vectors for S1G BIP
542ccf00b FT: Add control interface command to show configured RxKHs
392114a17 FT: Add dynamic reload of RxKH definitions from file
e94a7d794 FT: Move RxKH configuration clearing into a helper function
c69ce778f Fix building against OpenSSL 3
5589d62c5 nl80211: Avoid NL80211_WPA_VERSION_3 on older kernel versions
5ff6a2749 Remove the MLD specific exception for distinguishing EAPOL-Key msg 2 and 4
2314a3569 Testing functionality for EAPOL-Key Key Data field encryption
4abc37e67 Support Key Data field decryption for EAPOL-Key msg 2/4 and 4/4
f591732af Supplicant side testing functionality for EAPOL-Key Key Data field
f7a903654 Extend mechanism to distinguish EAPOL-Key msg 2/4 from 4/4
3547ed403 Authenticator side testing functionality for EAPOL-Key Key Data field
38719f113 Verify center frequency seg0/seg1 mapping result before use
acea0654f Initialize the variables before using it in channel update
af6e21faa P2P: Fix a logical error of workaround of extended listen failure
abc239a0b Get rid of multiple MIN macros
5290523db Apply a symmetrical bias against moving away from higher bands
05474b34b Decrease cross-threshold roam difficulty with bgscan_simple
73f06af14 wpa_supplicant: Do not invalidate PMKSA cache for bssid_* updates
05c167eb0 MLD: Fail connection if ML Authentication frame could not be parsed
a80dcf0e2 MLD: Read the correct BSSID from the RNR
906dade4f RRM: Handle scan TSF BSSID matching in context of MLD
12cdeb501 nl80211: Print driver name in debug output
197b440c4 nl80211: Print kernel version in debug output
5ae010aae nl80211: Avoid sending unsupported attributes
7fec9e7bc nl80211: Retrieve maxattr via genl for nl80211
f13683720 nl80211: Pass wiphy events to all affected interfaces
f9b3ecb0a DPP: Work arouind missing Auth Confirm ACK for testing
655794898 RRM: Fix the parsing of the Extended Request subelement in beacon req
b9983b35d MSCS: Process unsolciited MSCS Response frames
b427683bf MSCS: Extend MSCS response handling
db036b534 MSCS: Use a define for the MSCS Descriptor element fixed field length
2d83d224f Use ether_addr_equal() to compare whether two MAC addresses are equal
58027cfec WPS: Fix authorized MAC removal
95123ab3b Introduce ether_addr_equal()
76616a46b RSN: Fix (B)IGTK MLO KDE length print
f048e6626 wpa_supplicant: Don't assign pointer to bool
e0a2b3222 Fix compiler warnings on supplicant build with PASN but no FILS
8e8964cdb AP: Fix compilation warning in hapd_pasn_update_params()
a2fd63964 build: bgscan_simple depends on WNM
628f28610 trace: Fix compilation issue due to using an undefined symbol
cbcd056ec AP: Fix a typo in function name
594f85e30 mesh: Set the mld_link_id to -1 when adding a station
f40a58833 nl80211: Fix AP MLD MAC address on auth retry
38711a011 AP MLD: Remove link stations on a new station authentication
c6f519ff1 AP: Support deauthenticate/disassociate with MLD
9c937c889 AP: Move hostapd_ml_get_assoc_sta() to shared
ea401c168 AP MLD: Fix station lookup in hostapd_ml_get_assoc_sta()
e9f75a352 AP: Unify code handling deauthentication/disassociation
a1d7a9e3b build: Properly grab the libpcsclite cflags
9569315de Disable _FORTIFY_SOURCE when building with -O0
2112f0572 AP MLD: Correctly set the BSS parameters change count in RNR
0120d052d nl80211: Add NL80211_ATTR_MLO_LINK_ID for NL80211_CMD_REMAIN_ON_CHANNEL
bef417152 GAS: Accept GAS response using AP MLD MAC address
29814ee96 Extend pmf_in_use() to be aware of affiliated links on non-AP MLD
6ea81f323 nl80211: More detailed debug print for Management frame TX
9ccfc0d51 AP MLD: MLD address conversion for hostapd_drv_send_action_addr3_ap()
febb51bf8 AP MLD: Fix Association Response frame ACK handling
07f44a7c4 AP MLD: Prefer STA entry that has sta->wpa_sm initialized
0aeeaaaf1 Add QCA vendor command for flow policy configuration
064c233d1 AP: Fix a regression in indoor 6 GHz AP determination
196d6c83b Limit throughput estimation for HE 80/160 MHz based on VHT info
12c0f8ae3 Limit throughput estimation for HE 40 MHz based on HT info
98f3bd26d ACS: Extend the 320 MHz support
e6f2494c3 hostapd: Add eht_bw320_offset configuration option
733de8568 ACS: Fix not selecting the best channel in the segment
4881accbb ACS: Add HT40- support in the 2.4 GHz band
47e89935c dbus: Use PHY parameters from dbus or config for the GroupAdd command
fae12c4b1 Fix P2P_GROUP_ADD handling of the persistent group parameters
0143cf42c Move parse_freq() to be a common helper function
e3570f5e1 dbus: Use current_bss to get correct group BSSID and frequency on client
b91113e05 Support all PSK AKMs in case of AP mode PSK offload
4efb0247a Update definitions to point to the current IEEE 802.11 standard
121ccadeb AP: A helper function for determining whether the AP is an SP AP
24baffc8b AP: Share a common helper function for determining length of TPE elements
150ee0c06 AP: Add an additional TPE element when needed
3cbb3ac3f AP: Add TPE element for Indoor standard power AP
bcad7fec6 AP: Publish the correct PSD value in RNR TBTT information field
7065e5242 AP: Add configuration options for 6 GHz TPE Tx power
ada9083ac AP: Update the HE regulatory information AP types for the 6 GHz band
2d4f90521 RRM: Add support for including extended ID elements in beacon report
c88c08f0c SME: Remove comment in missing ML links handling
41e65efa3 ctrl_iface: Fix newline in print_ml()
a3a34c0eb HS 2.0: Remove useless debug print in non-Hotspot 2.0 cases
615835626 AP: Use the MLD MAC address for SAE authentication failures and testing
4a973718d Split hostapd_eid_rnr_iface() into two functions
0b55b8da3 AP: Add testing option to indicate an AP is disabled
2249b9f77 nl80211: Include disabled links indication in association command
5927455b8 MLD: Add support for disabled APs
981e8c1e4 nl80211: Add link ID to the queue parameters configuration print
409ebaaa1 AP: Support overriding EHT operation puncturing mask
26ad0be4f AP: Allow hex format for puncturing bitmap
799115a8e AP: Fix EHT MCS size validation for received element
14d7b9e37 ctrl_iface: Don't return -1 when dumping BSS information
12a957434 nl80211: Fix AP deinit path (link removal) in error cases
6c3438eef nl80211: Fix AP deinit path in error cases
6b7bb1001 Add QCA vendor attribute for EHT SCS traffic description support
f8657ea03 Add QCA vendor command for reporting firmware page fault informatin
ee00bbd29 Support VLAN offload with SAE password based selection
e748e50c6 SAE passwords from a separate file
40b255882 PASN: Select the latest available BSS entry for a BSSID
09d57e5f8 MBSSID: Element ID values in increasing order in Non-Inheritance element
88984bbb9 MLD STA: Update SAE PWE derivation in hunting-and-pecking loop case
c8dd70cfb Fix Multiple BSSID element length calculation
618df655a Use sta->vlan_id when needed for VLAN offload
afd306cf7 nl80211: Remove send_and_recv_msgs()
7c2f67cea nl80211: Add send_and_recv_resp() helper
ab506d777 nl80211: Add send_and_recv_cmd() helper
c73f9cde8 nl80211: Remove send_and_recv_msgs_connect_handle()
d2e6a395c nl80211: Do not set socket owner for NL80211_CMD_LEAVE_IBSS
35e58b741 nl80211: Remove send_and_recv_msgs_owner()
9823f4305 nl80211: Move control port attribute adding into more accurate location
0d619df8b nl80211: Use bss->nl_connect unconditionally
da0d51fee nl80211: Use socket cb instead of global->nl_cb in send_and_recv()
84fdc8cd8 nl80211: Accept NL80211_CMD_FRAME events in global context
828311ef3 AP MLD: More careful checking of Multi-Link element length fields
38a5ed5fd AP MLD: Skip unknown Multi-Link element subelements
62141825f tests: PASN authentication using driver event as trigger
2ab56694f Split ap_sta_set_authorized() into two steps
da8a38fec Remove unused assignment from Country element generation
33b5fc076 PKCS#1: Do not use pointer value after freeing
231d86ef9 OpenSSL: Check EVP_MAC_update() return value more consistently
a92694b00 OpenSSL: Check EVP_CIPHER_CTX_set_padding() return value more consistently
88bc6711a TDLS: Avoid unnecessary copying of the Link Identifier element
656cf50d8 More consistent sta pointer checks in handle_assoc()
29f38ebcf ACS: Check whether iface->current_mode is NULL before use
7fa840309 WNM: Skip current connection BSS when disassociate imminent is set
7a873c81e AP MLD: Do not schedule disconnection on BSS TM Request link removal
ec70d14f7 AP: MLD: Extend BSS transition management request for link removal
09988c435 WNM: Accept link removal BSS TM Request
80810929a WNM: Handle BTM request with Link Removal Imminent field set to 1
31e025c03 AP: When sending Action frames, use the AP MLD MAC address if needed
54e6c56d2 AP: Use AP MLD MAC address for terminating MLO association
7ee12fca4 WNM: Allow frames from AP MLD
0546f0e1b WNM: Use correct address when configured as AP MLD
1efdba5fd Handle PMKSA flush in the driver for SAE/OWE offload cases
6a793c5f2 bgscan: Fix bgscan_init() stub declaration
0af4c1478 hostapd: Check the bridge if ioctl SIOCBRADDIF fails
1b9006a8c Use the link BSSID to resolve current BSS for whether to roam check
c4dac077b wpa_supplicant: Remove redundant CONFIG_WNM in wnm_sta.c
d43a49a66 Remove a spurious tab in hostapd_eid_rnr()
a28ea8e51 AP: Fix setting MLD Parameters subfield in RNR element
07525cd5e Fix HE enabling for IBSS and mesh
40410c04f AP MLD: Channel switch for specific link
1b448c865 hostapd configuration file update using control interface
7dd7ae965 DFS: Change vht_capab according to user requested bandwidth
8920e0390 P2P: Force clearing of p2p-send-action radio work on P2P_STOP_FIND
8e294c3a2 P2P: Recover from successfully requested, but not started, listen
ceb7f65dc bgscan: Allow simple bgscan to do BTM queries
a83d3132e WNM: Define BSS transition management reason values
7ee7b046a nl80211: Use attribute NL80211_ATTR_BSSID to scan for specific BSSID
5bbc9462a tests: Test driver association ML link rejection flow
32434aa68 AP: Always include WPA_STA_AUTHORIZED in station flags mask
174a8fc41 AP MLD: Do not modify flags for link stations
0b5d11165 Fix MBO build with GAS dependency
58116877b EHT: Fix updating center freq segment 0 index for HE and VHT
40b04b703 Document Tunnel-Password encoding for passphrase/PSK
37d122c2f KaY: Make debug output consistent
f0cb82351 EHT: Add configuration for the EHT default PE duration
5a47bbc36 Add QCA vendor command to query transmit power information
9c2d6c423 Add QCA vendor attributes for link id for HT-scan/ACS command
7b6705579 Add QCA vendor commands for SDWF
20c82a270 Add an option to remove WMM-AC
32b5f7f50 Add an option to remove Robust AV (SCS, MSCS, QoS Management)
6ed8eba00 Add an option to remove RRM and supported operating class indication
4b80ad119 Populate the new beacon hint event to wpa_msg()
d8cae2d02 nl80211: Do not allow off channel when frequency is not specified
bbb0d3a40 mesh: Add for_each_sta implementation in wpa_auth_callbacks
4f69b4a31 mesh: Fix PMKSA cache entry addition with external PMKSA management
0302c3ad2 trace: binutils replaces bfd_hostptr_t with uintptr_t
033634019 Ignore missing set_secure_ranging_ctx callback for testing purposes
d54d0d898 AP MLD: Handle DFS in correct link
f1fee0d1f AP MLD: Handle channel switch event in correct link
fb6598864 nl80211: Add link ID when setting BSS attributes for AP MLD
5487d8d9e nl80211: Specify link ID when sending Management frames
859cbc396 nl80211: Remove links when stopping AP MLD in hostapd
780e72cc1 AP MLD: Do not include empty MLO KDEs
ecd9ea0c8 AP MLD: Do not access WPA authenticator object if not valid
21e8fcc80 nl80211: Add support for handling MLO removed links
cd79d834b trace: Add TEST_FAIL_TAG macro to allow more narrow matching
781e87c41 trace: Allow multiple failures in one test
e62d351ce trace: Document function pattern prefixes
5545d995b trace: Share common implementation for TEST_FAIL and TEST_ALLOC_FAIL
7d901dc7e trace: Use an array of skipped function names
e9bdecce4 Share TEST_FAIL/TEST_ALLOC_FAIL/GET_FAIL/GET_ALLOC_FAIL handler
2c89ca922 wpa_supplicant: Use wpa_msg() in bssid_ignore.c
6fc2d1357 AP: Get rid of wpa_auth_pmksa_add3()
e99670420 AP: Handle re-association from a non-AP MLD
a18f8ee0f AP MLD: Use MLD MAC address for SA query and response when needed
b9c81e200 MLD: Use MLD MAC address for deauthentication
f60287e6c AP: Avoid setting same MLD and link address
e5917e2a5 scan: MLD: Include SSID in ML probe request
b29ac99d5 scan: Include AP MLD ID in ML probe request if needed
d64ec9414 AP MLD: Don't include AP MLD ID in Beacon frames
3bde81175 ML: Add basic handling of ML probe requests
ecb22ba12 AP: MLO: Add helper to iterate all links of an AP MLD
db2bc0364 AP: Add parsing of ML probe requests
82453a348 AP: Split Probe Response frame IE generation into a separate function
6b5e00a80 AP: Use a struct for Probe Response generation in/out params
3cd377eb5 MLD: Ignore failed links from association attempt
4a1cd7f54 nl80211: Report link specific association failures from the kernel
6ba9b9440 nl80211: Add support to parse out link from error reply
e6eebd0d5 MLD: Do not consider ignored BSSs for links when parsing RNR info
92d8d1d76 MLD: Add a TEST_FAIL to fail one link in an MLD association.
846e65c7c MLD: Return status code for links when rejecting association
5af986c75 MLD: Also mark links as failed after association failure
c55a272f6 EHT: Define status codes from IEEE P802.11be/D4.0
d95838b79 AP: Add support for testing ML link removal
73a6f5c37 AP MLD: Make BSS parameter change variable
9160540ec wpa_supplicant: Fix ml_ie_len type in wpa_bss_parse_basic_ml_element()
cb90aa3ac wpa_supplicant: Remove duplicate logic in wpas_ml_element()
74b688430 wpa_supplicant: Add config parameters for MLD testing
6220fb52e dbus: Increase XML buffer size for an interface introspection data
b3aafd5a8 common: Simplify and avoid confusing defragmentation API
0f7d15dd3 ieee802_11_defs: Fix EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID
9ffebf758 SME: Drop old disassoc_while_authenticating workaround
645ec9b58 nl80211: Do a roundtrip to reset event supressions
c8b4ad70b tests: Set wpa_s->global for module tests
69ea73bfe nl80211: Update port authorized indication for MLO address
6f014c0d0 ACS: Add 320 MHz support for EHT
f0aea885f EHT: Fix HE Channel Center Freq Seg0/Seg1 for 6 GHz 320 MHz cases
07c03a655 EHT: Fix conditions for including EHT Operation Information field
01d95b75b SAE: prime_len means length in bytes not bits
a02585cef MBSSID: Use BIGTK from the transmitted BSS for beacon protection
a768556f7 Prefer SAE over PSK in WPA3-Personal transition mode cases
43184bf06 OWE: Optimize transition mode AP scan with owe_only=1 STA configuration
4ff287e92 P2P: Do not skip updating BSS table when frequency is changed
73b49016f Fix references to correct driver capability flag for PSK/OWE offloads
415839406 OpenSSL: Allow openssl_ciphers override with Suite B config on server
e9b13938a Add EHT mode support for ratemask configuration vendor command
239469eb1 Define new command in qca_tsf_cmd to get AP channel switch TSF time
Change-Id: I1540a39cd343b6fb9915fcc78e3f6bd2cb8eb0d9
Signed-off-by: Sunil Ravi <sunilravi@google.com>
diff --git a/src/ap/acs.c b/src/ap/acs.c
index e3cfe1d..28b0ba7 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -245,6 +245,8 @@
ACS_BW40,
ACS_BW80,
ACS_BW160,
+ ACS_BW320_1,
+ ACS_BW320_2,
};
struct bw_item {
@@ -286,10 +288,20 @@
{ 6435, 6575, 111 }, { 6595, 6735, 143 },
{ 6755, 6895, 175 }, { 6915, 7055, 207 }, { -1, -1, -1 }
};
+static const struct bw_item bw_320_1[] = {
+ { 5955, 6255, 31 }, { 6275, 6575, 95 }, { 6595, 6895, 159 },
+ { -1, -1, -1 }
+};
+static const struct bw_item bw_320_2[] = {
+ { 6115, 6415, 63 }, { 6435, 6735, 127 }, { 6755, 7055, 191 },
+ { -1, -1, -1 }
+};
static const struct bw_item *bw_desc[] = {
[ACS_BW40] = bw_40,
[ACS_BW80] = bw_80,
[ACS_BW160] = bw_160,
+ [ACS_BW320_1] = bw_320_1,
+ [ACS_BW320_2] = bw_320_2,
};
@@ -768,6 +780,42 @@
#endif /* CONFIG_IEEE80211BE */
+static bool
+acs_usable_bw320_chan(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan, int *bw320_offset)
+{
+ const char *bw320_str[] = { "320 MHz", "320 MHz-1", "320 MHz-2" };
+ int conf_bw320_offset = hostapd_get_bw320_offset(iface->conf);
+
+ *bw320_offset = 0;
+ switch (conf_bw320_offset) {
+ case 1:
+ if (acs_usable_bw_chan(chan, ACS_BW320_1))
+ *bw320_offset = 1;
+ break;
+ case 2:
+ if (acs_usable_bw_chan(chan, ACS_BW320_2))
+ *bw320_offset = 2;
+ break;
+ case 0:
+ default:
+ conf_bw320_offset = 0;
+ if (acs_usable_bw_chan(chan, ACS_BW320_1))
+ *bw320_offset = 1;
+ else if (acs_usable_bw_chan(chan, ACS_BW320_2))
+ *bw320_offset = 2;
+ break;
+ }
+
+ if (!*bw320_offset)
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: not allowed as primary channel for %s bandwidth",
+ chan->chan, bw320_str[conf_bw320_offset]);
+
+ return *bw320_offset != 0;
+}
+
+
static void
acs_find_ideal_chan_mode(struct hostapd_iface *iface,
struct hostapd_hw_modes *mode,
@@ -779,14 +827,18 @@
struct hostapd_channel_data *chan, *adj_chan = NULL, *best;
long double factor;
int i, j;
+ int bw320_offset = 0, ideal_bw320_offset = 0;
unsigned int k;
+ int secondary_channel = 1, freq_offset;
+
+ if (is_24ghz_mode(mode->mode))
+ secondary_channel = iface->conf->secondary_channel;
for (i = 0; i < mode->num_channels; i++) {
- double total_weight;
+ double total_weight = 0;
struct acs_bias *bias, tmp_bias;
- bool update_best = true;
- best = chan = &mode->channels[i];
+ chan = &mode->channels[i];
/* Since in the current ACS implementation the first channel is
* always a primary channel, skip channels not available as
@@ -818,7 +870,7 @@
iface->conf->country[2] == 0x4f)
continue;
- if (!chan_bw_allowed(chan, bw, 1, 1)) {
+ if (!chan_bw_allowed(chan, bw, secondary_channel != -1, 1)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: BW %u is not supported",
chan->chan, bw);
@@ -839,7 +891,8 @@
}
if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
- (iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
+ (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
+ iface->conf->ieee80211be)) {
if (hostapd_get_oper_chwidth(iface->conf) ==
CONF_OPER_CHWIDTH_80MHZ &&
!acs_usable_bw_chan(chan, ACS_BW80)) {
@@ -859,13 +912,25 @@
}
}
+ if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ iface->conf->ieee80211be) {
+ if (hostapd_get_oper_chwidth(iface->conf) ==
+ CONF_OPER_CHWIDTH_320MHZ &&
+ !acs_usable_bw320_chan(iface, chan, &bw320_offset))
+ continue;
+ }
+
factor = 0;
- if (acs_usable_chan(chan))
+ best = NULL;
+ if (acs_usable_chan(chan)) {
factor = chan->interference_factor;
- total_weight = 1;
+ total_weight = 1;
+ best = chan;
+ }
for (j = 1; j < n_chans; j++) {
- adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
+ adj_chan = acs_find_chan(iface, chan->freq +
+ j * secondary_channel * 20);
if (!adj_chan)
break;
@@ -876,16 +941,14 @@
break;
}
- if (acs_usable_chan(adj_chan)) {
- factor += adj_chan->interference_factor;
- total_weight += 1;
- } else {
- update_best = false;
- }
+ if (!acs_usable_chan(adj_chan))
+ continue;
+
+ factor += adj_chan->interference_factor;
+ total_weight += 1;
/* find the best channel in this segment */
- if (update_best &&
- adj_chan->interference_factor <
+ if (!best || adj_chan->interference_factor <
best->interference_factor)
best = adj_chan;
}
@@ -898,8 +961,9 @@
/* If the AP is in the 5 GHz or 6 GHz band, lets prefer a less
* crowded primary channel if one was found in the segment */
- if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
- chan != best) {
+ if (iface->current_mode &&
+ iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ best && chan != best) {
wpa_printf(MSG_DEBUG,
"ACS: promoting channel %d over %d (less interference %Lg/%Lg)",
best->chan, chan->chan,
@@ -912,8 +976,9 @@
* channel interference factor. */
if (is_24ghz_mode(mode->mode)) {
for (j = 0; j < n_chans; j++) {
+ freq_offset = j * 20 * secondary_channel;
adj_chan = acs_find_chan(iface, chan->freq +
- (j * 20) - 5);
+ freq_offset - 5);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_ADJ_WEIGHT *
adj_chan->interference_factor;
@@ -921,7 +986,7 @@
}
adj_chan = acs_find_chan(iface, chan->freq +
- (j * 20) - 10);
+ freq_offset - 10);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_NEXT_ADJ_WEIGHT *
adj_chan->interference_factor;
@@ -929,7 +994,7 @@
}
adj_chan = acs_find_chan(iface, chan->freq +
- (j * 20) + 5);
+ freq_offset + 5);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_ADJ_WEIGHT *
adj_chan->interference_factor;
@@ -937,7 +1002,7 @@
}
adj_chan = acs_find_chan(iface, chan->freq +
- (j * 20) + 10);
+ freq_offset + 10);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_NEXT_ADJ_WEIGHT *
adj_chan->interference_factor;
@@ -946,6 +1011,9 @@
}
}
+ if (total_weight == 0)
+ continue;
+
factor /= total_weight;
bias = NULL;
@@ -983,6 +1051,7 @@
*ideal_factor = factor;
*ideal_chan = chan;
+ ideal_bw320_offset = bw320_offset;
#ifdef CONFIG_IEEE80211BE
if (iface->conf->ieee80211be)
@@ -993,9 +1062,13 @@
}
/* This channel would at least be usable */
- if (!(*rand_chan))
+ if (!(*rand_chan)) {
*rand_chan = chan;
+ ideal_bw320_offset = bw320_offset;
+ }
}
+
+ hostapd_set_and_check_bw320_offset(iface->conf, ideal_bw320_offset);
}
@@ -1022,19 +1095,12 @@
goto bw_selected;
}
- /* TODO: HT40- support */
-
- if (iface->conf->ieee80211n &&
- iface->conf->secondary_channel == -1) {
- wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
- return NULL;
- }
-
if (iface->conf->ieee80211n &&
iface->conf->secondary_channel)
n_chans = 2;
- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
+ iface->conf->ieee80211be) {
switch (hostapd_get_oper_chwidth(iface->conf)) {
case CONF_OPER_CHWIDTH_80MHZ:
n_chans = 4;
@@ -1042,6 +1108,9 @@
case CONF_OPER_CHWIDTH_160MHZ:
n_chans = 8;
break;
+ case CONF_OPER_CHWIDTH_320MHZ:
+ n_chans = 16;
+ break;
default:
break;
}
@@ -1091,7 +1160,8 @@
acs_find_mode(iface, iface->freq) != HOSTAPD_MODE_IEEE80211A)
return;
- wpa_printf(MSG_DEBUG, "ACS: Adjusting HT/VHT/HE secondary frequency");
+ wpa_printf(MSG_DEBUG,
+ "ACS: Adjusting HT/VHT/HE/EHT secondary frequency");
for (i = 0; bw_desc[ACS_BW40][i].first != -1; i++) {
if (iface->freq == bw_desc[ACS_BW40][i].first)
@@ -1106,7 +1176,7 @@
{
int center;
- wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
+ wpa_printf(MSG_DEBUG, "ACS: Adjusting center frequency");
switch (hostapd_get_oper_chwidth(iface->conf)) {
case CONF_OPER_CHWIDTH_USE_HT:
@@ -1125,11 +1195,28 @@
case CONF_OPER_CHWIDTH_160MHZ:
center = acs_get_bw_center_chan(iface->freq, ACS_BW160);
break;
+ case CONF_OPER_CHWIDTH_320MHZ:
+ switch (hostapd_get_bw320_offset(iface->conf)) {
+ case 1:
+ center = acs_get_bw_center_chan(iface->freq,
+ ACS_BW320_1);
+ break;
+ case 2:
+ center = acs_get_bw_center_chan(iface->freq,
+ ACS_BW320_2);
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "ACS: BW320 offset is not selected");
+ return;
+ }
+
+ break;
default:
/* TODO: How can this be calculated? Adjust
* acs_find_ideal_chan() */
wpa_printf(MSG_INFO,
- "ACS: Only VHT20/40/80/160 is supported now");
+ "ACS: Only VHT20/40/80/160/320 is supported now");
return;
}
@@ -1192,7 +1279,8 @@
iface->conf->punct_bitmap = ideal_chan->punct_bitmap;
#endif /* CONFIG_IEEE80211BE */
- if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
+ if (iface->conf->ieee80211ac || iface->conf->ieee80211ax ||
+ iface->conf->ieee80211be) {
acs_adjust_secondary(iface);
acs_adjust_center_freq(iface);
}
diff --git a/src/ap/airtime_policy.c b/src/ap/airtime_policy.c
index abe817c..6844311 100644
--- a/src/ap/airtime_policy.c
+++ b/src/ap/airtime_policy.c
@@ -232,7 +232,7 @@
struct airtime_sta_weight *wt;
wt = hapd->conf->airtime_weight_list;
- while (wt && os_memcmp(wt->addr, sta, ETH_ALEN) != 0)
+ while (wt && !ether_addr_equal(wt->addr, sta))
wt = wt->next;
return wt ? wt->weight : hapd->conf->airtime_weight;
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 60d3566..445d963 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1,6 +1,6 @@
/*
* hostapd / Configuration helper functions
- * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -165,6 +165,7 @@
#ifdef CONFIG_TESTING_OPTIONS
bss->sae_commit_status = -1;
+ bss->test_assoc_comeback_type = -1;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_PASN
@@ -283,6 +284,10 @@
conf->he_6ghz_max_ampdu_len_exp = 7;
conf->he_6ghz_rx_ant_pat = 1;
conf->he_6ghz_tx_ant_pat = 1;
+ conf->he_6ghz_reg_pwr_type = HE_REG_INFO_6GHZ_AP_TYPE_VLP;
+ conf->reg_def_cli_eirp_psd = -1;
+ conf->reg_sub_cli_eirp_psd = -1;
+ conf->reg_def_cli_eirp = -1;
#endif /* CONFIG_IEEE80211AX */
/* The third octet of the country string uses an ASCII space character
@@ -297,6 +302,8 @@
conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
#endif /* CONFIG_AIRTIME_POLICY */
+ hostapd_set_and_check_bw320_offset(conf, 0);
+
return conf;
}
@@ -695,6 +702,33 @@
}
+#ifdef CONFIG_IEEE80211R_AP
+
+void hostapd_config_clear_rxkhs(struct hostapd_bss_config *conf)
+{
+ struct ft_remote_r0kh *r0kh, *r0kh_prev;
+ struct ft_remote_r1kh *r1kh, *r1kh_prev;
+
+ r0kh = conf->r0kh_list;
+ conf->r0kh_list = NULL;
+ while (r0kh) {
+ r0kh_prev = r0kh;
+ r0kh = r0kh->next;
+ os_free(r0kh_prev);
+ }
+
+ r1kh = conf->r1kh_list;
+ conf->r1kh_list = NULL;
+ while (r1kh) {
+ r1kh_prev = r1kh;
+ r1kh = r1kh->next;
+ os_free(r1kh_prev);
+ }
+}
+
+#endif /* CONFIG_IEEE80211R_AP */
+
+
static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf)
{
struct anqp_element *elem;
@@ -827,26 +861,9 @@
os_free(conf->time_zone);
#ifdef CONFIG_IEEE80211R_AP
- {
- struct ft_remote_r0kh *r0kh, *r0kh_prev;
- struct ft_remote_r1kh *r1kh, *r1kh_prev;
-
- r0kh = conf->r0kh_list;
- conf->r0kh_list = NULL;
- while (r0kh) {
- r0kh_prev = r0kh;
- r0kh = r0kh->next;
- os_free(r0kh_prev);
- }
-
- r1kh = conf->r1kh_list;
- conf->r1kh_list = NULL;
- while (r1kh) {
- r1kh_prev = r1kh;
- r1kh = r1kh->next;
- os_free(r1kh_prev);
- }
- }
+ hostapd_config_clear_rxkhs(conf);
+ os_free(conf->rxkh_file);
+ conf->rxkh_file = NULL;
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_WPS
@@ -944,6 +961,8 @@
wpabuf_free(conf->rsnxe_override_ft);
wpabuf_free(conf->gtk_rsc_override);
wpabuf_free(conf->igtk_rsc_override);
+ wpabuf_free(conf->eapol_m1_elements);
+ wpabuf_free(conf->eapol_m3_elements);
#endif /* CONFIG_TESTING_OPTIONS */
os_free(conf->no_probe_resp_if_seen_on);
@@ -1131,10 +1150,9 @@
for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) {
if (next_ok &&
(psk->group ||
- (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) ||
+ (addr && ether_addr_equal(psk->addr, addr)) ||
(!addr && p2p_dev_addr &&
- os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
- 0))) {
+ ether_addr_equal(psk->p2p_dev_addr, p2p_dev_addr)))) {
if (vlan_id)
*vlan_id = psk->vlan_id;
return psk->psk;
@@ -1558,6 +1576,10 @@
"Cannot set ieee80211be without ieee80211ax");
return -1;
}
+
+ if (full_config)
+ hostapd_set_and_check_bw320_offset(conf,
+ conf->eht_bw320_offset);
#endif /* CONFIG_IEEE80211BE */
if (full_config && conf->mbssid && !conf->ieee80211ax) {
@@ -1750,7 +1772,7 @@
int i = 0;
while (i < *num) {
- if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal((*acl)[i].addr, addr)) {
os_remove_in_array(*acl, *num, sizeof(**acl), i);
(*num)--;
} else {
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 99a6d18..69db16d 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1,6 +1,6 @@
/*
* hostapd / Configuration definitions and helpers functions
- * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -405,6 +405,7 @@
int ft_over_ds;
int ft_psk_generate_local;
int r1_max_key_lifetime;
+ char *rxkh_file;
#endif /* CONFIG_IEEE80211R_AP */
char *ctrl_interface; /* directory for UNIX domain sockets */
@@ -704,6 +705,14 @@
unsigned int oci_freq_override_ft_assoc;
unsigned int oci_freq_override_fils_assoc;
unsigned int oci_freq_override_wnm_sleep;
+ struct wpabuf *eapol_m1_elements;
+ struct wpabuf *eapol_m3_elements;
+ bool eapol_m3_no_encrypt;
+ int test_assoc_comeback_type;
+
+#ifdef CONFIG_IEEE80211BE
+ u16 eht_oper_puncturing_override;
+#endif /* CONFIG_IEEE80211BE */
#endif /* CONFIG_TESTING_OPTIONS */
#define MESH_ENABLED BIT(0)
@@ -948,6 +957,14 @@
/* The AP's MLD MAC address within the AP MLD */
u8 mld_addr[ETH_ALEN];
+
+#ifdef CONFIG_TESTING_OPTIONS
+ /*
+ * If set indicate the AP as disabled in the RNR element included in the
+ * other APs in the AP MLD.
+ */
+ bool mld_indicate_disabled;
+#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_IEEE80211BE */
};
@@ -1139,6 +1156,19 @@
u8 he_6ghz_rx_ant_pat;
u8 he_6ghz_tx_ant_pat;
u8 he_6ghz_reg_pwr_type;
+
+ int reg_def_cli_eirp_psd;
+ int reg_sub_cli_eirp_psd;
+
+ /*
+ * This value should be used when regulatory client EIRP PSD values
+ * advertised by an AP that is an SP AP or an indoor SP AP are
+ * insufficient to ensure that regulatory client limits on total EIRP
+ * are always met for all transmission bandwidths within the bandwidth
+ * of the AP’s BSS.
+ */
+ int reg_def_cli_eirp;
+
bool require_he;
#endif /* CONFIG_IEEE80211AX */
@@ -1175,6 +1205,8 @@
struct eht_phy_capabilities_info eht_phy_capab;
u16 punct_bitmap; /* a bitmap of disabled 20 MHz channels */
u8 punct_acs_threshold;
+ u8 eht_default_pe_duration;
+ u8 eht_bw320_offset;
#endif /* CONFIG_IEEE80211BE */
/* EHT enable/disable config from CHAN_SWITCH */
@@ -1242,7 +1274,8 @@
#ifdef CONFIG_IEEE80211BE
if (conf->ieee80211be)
conf->eht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
- if (center_idx_to_bw_6ghz(oper_centr_freq_seg0_idx) == 4)
+ if (is_6ghz_op_class(conf->op_class) &&
+ center_idx_to_bw_6ghz(oper_centr_freq_seg0_idx) == 4)
oper_centr_freq_seg0_idx +=
conf->channel > oper_centr_freq_seg0_idx ? 16 : -16;
#endif /* CONFIG_IEEE80211BE */
@@ -1274,6 +1307,43 @@
conf->vht_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
}
+static inline u8
+hostapd_get_bw320_offset(struct hostapd_config *conf)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (conf->ieee80211be && is_6ghz_op_class(conf->op_class) &&
+ hostapd_get_oper_chwidth(conf) == CONF_OPER_CHWIDTH_320MHZ)
+ return conf->eht_bw320_offset;
+#endif /* CONFIG_IEEE80211BE */
+ return 0;
+}
+
+static inline void
+hostapd_set_and_check_bw320_offset(struct hostapd_config *conf,
+ u8 bw320_offset)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (conf->ieee80211be && is_6ghz_op_class(conf->op_class) &&
+ op_class_to_ch_width(conf->op_class) == CONF_OPER_CHWIDTH_320MHZ) {
+ if (conf->channel) {
+ /* If the channel is set, then calculate bw320_offset
+ * by center frequency segment 0.
+ */
+ u8 seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
+
+ conf->eht_bw320_offset = (seg0 - 31) % 64 ? 2 : 1;
+ } else {
+ /* If the channel is not set, bw320_offset indicates
+ * preferred offset of 320 MHz.
+ */
+ conf->eht_bw320_offset = bw320_offset;
+ }
+ } else {
+ conf->eht_bw320_offset = 0;
+ }
+#endif /* CONFIG_IEEE80211BE */
+}
+
int hostapd_mac_comp(const void *a, const void *b);
struct hostapd_config * hostapd_config_defaults(void);
@@ -1282,6 +1352,7 @@
void hostapd_config_free_eap_user(struct hostapd_eap_user *user);
void hostapd_config_free_eap_users(struct hostapd_eap_user *user);
void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
+void hostapd_config_clear_rxkhs(struct hostapd_bss_config *conf);
void hostapd_config_free_bss(struct hostapd_bss_config *conf);
void hostapd_config_free(struct hostapd_config *conf);
int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 8f9cc5b..60d66e4 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -265,9 +265,35 @@
}
+static bool hostapd_sta_is_link_sta(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta) &&
+ sta->mld_assoc_link_id != hapd->mld_link_id)
+ return true;
+#endif /* CONFIG_IEEE80211BE */
+
+ return false;
+}
+
+
int hostapd_set_authorized(struct hostapd_data *hapd,
struct sta_info *sta, int authorized)
{
+ /*
+ * The WPA_STA_AUTHORIZED flag is relevant only for the MLD station and
+ * not to the link stations (as the authorization is done between the
+ * MLD peers). Thus, do not propagate the change to the driver for the
+ * link stations.
+ */
+ if (hostapd_sta_is_link_sta(hapd, sta)) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Do not update link station flags (" MACSTR ")",
+ __func__, MAC2STR(sta->addr));
+ return 0;
+ }
+
if (authorized) {
return hostapd_sta_set_flags(hapd, sta->addr,
hostapd_sta_flags_to_drv(
@@ -285,11 +311,24 @@
{
int set_flags, total_flags, flags_and, flags_or;
total_flags = hostapd_sta_flags_to_drv(sta->flags);
- set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP;
- if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
- sta->auth_alg == WLAN_AUTH_FT) &&
- sta->flags & WLAN_STA_AUTHORIZED)
- set_flags |= WPA_STA_AUTHORIZED;
+ set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP |
+ WPA_STA_AUTHORIZED;
+
+ /*
+ * All the station flags other than WPA_STA_SHORT_PREAMBLE are relevant
+ * only for the MLD station and not to the link stations (as these flags
+ * are related to the MLD state and not the link state). As for the
+ * WPA_STA_SHORT_PREAMBLE, since the station is an EHT station, it must
+ * support short preamble. Thus, do not propagate the change to the
+ * driver for the link stations.
+ */
+ if (hostapd_sta_is_link_sta(hapd, sta)) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Do not update link station flags (" MACSTR ")",
+ __func__, MAC2STR(sta->addr));
+ return 0;
+ }
+
flags_or = total_flags & set_flags;
flags_and = total_flags | ~set_flags;
return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
@@ -793,15 +832,21 @@
const u8 *addr, int reason)
{
int link_id = -1;
+ const u8 *own_addr = hapd->own_addr;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap)
+ if (hapd->conf->mld_ap) {
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
link_id = hapd->mld_link_id;
+ if (ap_sta_is_mld(hapd, sta))
+ own_addr = hapd->mld_addr;
+ }
#endif /* CONFIG_IEEE80211BE */
if (!hapd->driver || !hapd->driver->sta_deauth || !hapd->drv_priv)
return 0;
- return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
+ return hapd->driver->sta_deauth(hapd->drv_priv, own_addr, addr,
reason, link_id);
}
@@ -809,9 +854,20 @@
int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
const u8 *addr, int reason)
{
+ const u8 *own_addr = hapd->own_addr;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap) {
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+ if (ap_sta_is_mld(hapd, sta))
+ own_addr = hapd->mld_addr;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv)
return 0;
- return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
+ return hapd->driver->sta_disassoc(hapd->drv_priv, own_addr, addr,
reason);
}
@@ -826,22 +882,22 @@
}
-int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
- unsigned int wait, const u8 *dst, const u8 *data,
- size_t len)
+static int hapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+ unsigned int wait, const u8 *dst,
+ const u8 *data, size_t len, bool addr3_ap)
{
+ const u8 *own_addr = hapd->own_addr;
const u8 *bssid;
const u8 wildcard_bssid[ETH_ALEN] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
+ struct sta_info *sta;
if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv)
return 0;
bssid = hapd->own_addr;
- if (!is_multicast_ether_addr(dst) &&
+ if (!addr3_ap && !is_multicast_ether_addr(dst) &&
len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
- struct sta_info *sta;
-
/*
* Public Action frames to a STA that is not a member of the BSS
* shall use wildcard BSSID value.
@@ -849,7 +905,7 @@
sta = ap_get_sta(hapd, dst);
if (!sta || !(sta->flags & WLAN_STA_ASSOC))
bssid = wildcard_bssid;
- } else if (is_broadcast_ether_addr(dst) &&
+ } else if (!addr3_ap && is_broadcast_ether_addr(dst) &&
len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
/*
* The only current use case of Public Action frames with
@@ -858,9 +914,27 @@
* so have to use the wildcard BSSID value.
*/
bssid = wildcard_bssid;
+#ifdef CONFIG_IEEE80211BE
+ } else if (hapd->conf->mld_ap) {
+ sta = ap_get_sta(hapd, dst);
+
+ if (ap_sta_is_mld(hapd, sta)) {
+ own_addr = hapd->mld_addr;
+ bssid = own_addr;
+ }
+#endif /* CONFIG_IEEE80211BE */
}
+
return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
- hapd->own_addr, bssid, data, len, 0);
+ own_addr, bssid, data, len, 0);
+}
+
+
+int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
+ unsigned int wait, const u8 *dst, const u8 *data,
+ size_t len)
+{
+ return hapd_drv_send_action(hapd, freq, wait, dst, data, len, false);
}
@@ -869,11 +943,7 @@
unsigned int wait, const u8 *dst,
const u8 *data, size_t len)
{
- if (hapd->driver == NULL || hapd->driver->send_action == NULL)
- return 0;
- return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
- hapd->own_addr, hapd->own_addr, data,
- len, 0);
+ return hapd_drv_send_action(hapd, freq, wait, dst, data, len, true);
}
@@ -1025,6 +1095,12 @@
os_memset(¶ms, 0, sizeof(params));
params.hw_mode = hapd->iface->conf->hw_mode;
+ params.link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && hapd->iconf->ieee80211be &&
+ !hapd->conf->disable_11be)
+ params.link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
/*
* If no chanlist config parameter is provided, include all enabled
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
index 20be7f8..13facab 100644
--- a/src/ap/ap_list.c
+++ b/src/ap/ap_list.c
@@ -55,7 +55,7 @@
struct ap_info *s;
s = iface->ap_hash[STA_HASH(ap)];
- while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0)
+ while (s != NULL && !ether_addr_equal(s->addr, ap))
s = s->hnext;
return s;
}
@@ -100,13 +100,13 @@
s = iface->ap_hash[STA_HASH(ap->addr)];
if (s == NULL) return;
- if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(s->addr, ap->addr)) {
iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
return;
}
while (s->hnext != NULL &&
- os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0)
+ !ether_addr_equal(s->hnext->addr, ap->addr))
s = s->hnext;
if (s->hnext != NULL)
s->hnext = s->hnext->hnext;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 1b5cea9..e50f0a0 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -239,12 +239,10 @@
continue; /* can use same entry */
}
- if (start && prev) {
+ if (start && prev)
pos = hostapd_eid_country_add(hapd, pos, end,
chan_spacing,
start, prev);
- start = NULL;
- }
/* Start new group */
start = prev = chan;
@@ -565,19 +563,78 @@
}
-static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *req,
- int is_p2p, size_t *resp_len,
- const u8 *known_bss, u8 known_bss_len)
+static size_t he_elem_len(struct hostapd_data *hapd)
{
+ size_t len = 0;
+
+#ifdef CONFIG_IEEE80211AX
+ if (!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
+ return len;
+
+ len += 3 + sizeof(struct ieee80211_he_capabilities) +
+ 3 + sizeof(struct ieee80211_he_operation) +
+ 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
+ 3 + sizeof(struct ieee80211_spatial_reuse);
+ if (is_6ghz_op_class(hapd->iconf->op_class)) {
+ len += sizeof(struct ieee80211_he_6ghz_oper_info) +
+ 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
+ /* An additional Transmit Power Envelope element for
+ * subordinate client */
+ if (he_reg_is_indoor(hapd->iconf->he_6ghz_reg_pwr_type))
+ len += 4;
+
+ /* An additional Transmit Power Envelope element for
+ * default client with unit interpretation of regulatory
+ * client EIRP */
+ if (hapd->iconf->reg_def_cli_eirp != -1 &&
+ he_reg_is_sp(hapd->iconf->he_6ghz_reg_pwr_type))
+ len += 4;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
+ return len;
+}
+
+
+struct probe_resp_params {
+ const struct ieee80211_mgmt *req;
+ bool is_p2p;
+
+ /* Generated IEs will be included inside an ML element */
+ bool is_ml_sta_info;
+ struct hostapd_data *mld_ap;
+ struct mld_info *mld_info;
+
struct ieee80211_mgmt *resp;
- u8 *pos, *epos, *csa_pos;
- size_t buflen;
+ size_t resp_len;
+ u8 *csa_pos;
+ u8 *ecsa_pos;
+ const u8 *known_bss;
+ u8 known_bss_len;
- hapd = hostapd_mbssid_get_tx_bss(hapd);
+#ifdef CONFIG_IEEE80211AX
+ u8 *cca_pos;
+#endif /* CONFIG_IEEE80211AX */
+};
-#define MAX_PROBERESP_LEN 768
- buflen = MAX_PROBERESP_LEN;
+
+static void hostapd_free_probe_resp_params(struct probe_resp_params *params)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (!params)
+ return;
+ ap_sta_free_sta_profile(params->mld_info);
+ os_free(params->mld_info);
+ params->mld_info = NULL;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+static size_t hostapd_probe_resp_elems_len(struct hostapd_data *hapd,
+ struct probe_resp_params *params)
+{
+ size_t buflen = 0;
+
#ifdef CONFIG_WPS
if (hapd->wps_probe_resp_ie)
buflen += wpabuf_len(hapd->wps_probe_resp_ie);
@@ -597,23 +654,7 @@
2 + sizeof(struct ieee80211_vht_operation);
}
-#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
- buflen += 3 + sizeof(struct ieee80211_he_capabilities) +
- 3 + sizeof(struct ieee80211_he_operation) +
- 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
- 3 + sizeof(struct ieee80211_spatial_reuse);
- if (is_6ghz_op_class(hapd->iconf->op_class)) {
- buflen += sizeof(struct ieee80211_he_6ghz_oper_info) +
- 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
- /* An additional Transmit Power Envelope element for
- * subordinate client */
- if (hapd->iconf->he_6ghz_reg_pwr_type ==
- HE_6GHZ_INDOOR_AP)
- buflen += 4;
- }
- }
-#endif /* CONFIG_IEEE80211AX */
+ buflen += he_elem_len(hapd);
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
@@ -622,58 +663,45 @@
if (hapd->iconf->punct_bitmap)
buflen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
- /*
- * TODO: Multi-Link element has variable length and can be
- * long based on the common info and number of per
- * station profiles. For now use 256.
- */
- if (hapd->conf->mld_ap)
- buflen += 256;
+ if (!params->is_ml_sta_info && hapd->conf->mld_ap) {
+ struct hostapd_data *ml_elem_ap =
+ params->mld_ap ? params->mld_ap : hapd;
+
+ buflen += hostapd_eid_eht_ml_beacon_len(
+ ml_elem_ap, params->mld_info, !!params->mld_ap);
+ }
}
#endif /* CONFIG_IEEE80211BE */
buflen += hostapd_eid_mbssid_len(hapd, WLAN_FC_STYPE_PROBE_RESP, NULL,
- known_bss, known_bss_len, NULL);
- buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
+ 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_mbo_ie_len(hapd);
buflen += hostapd_eid_owe_trans_len(hapd);
buflen += hostapd_eid_dpp_cc_len(hapd);
- resp = os_zalloc(buflen);
- if (resp == NULL)
- return NULL;
+ return buflen;
+}
- epos = ((u8 *) resp) + MAX_PROBERESP_LEN;
- resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_PROBE_RESP);
- /* Unicast the response to all requests on bands other than 6 GHz. For
- * the 6 GHz, unicast is used only if the actual SSID is not included in
- * the Beacon frames. Otherwise, broadcast response is used per IEEE
- * Std 802.11ax-2021, 26.17.2.3.2. Broadcast address is also used for
- * the Probe Response frame template for the unsolicited (i.e., not as
- * a response to a specific request) case. */
- if (req && (!is_6ghz_op_class(hapd->iconf->op_class) ||
- hapd->conf->ignore_broadcast_ssid))
- os_memcpy(resp->da, req->sa, ETH_ALEN);
- else
- os_memset(resp->da, 0xff, ETH_ALEN);
+static u8 * hostapd_probe_resp_fill_elems(struct hostapd_data *hapd,
+ struct probe_resp_params *params,
+ u8 *pos, size_t len)
+{
+ u8 *csa_pos;
+ u8 *epos;
- os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+ epos = pos + len;
- os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
- resp->u.probe_resp.beacon_int =
- host_to_le16(hapd->iconf->beacon_int);
-
- /* hardware or low-level driver will setup seq_ctrl and timestamp */
- resp->u.probe_resp.capab_info =
- host_to_le16(hostapd_own_capab_info(hapd));
-
- pos = resp->u.probe_resp.variable;
- *pos++ = WLAN_EID_SSID;
- *pos++ = hapd->conf->ssid.ssid_len;
- os_memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len);
- pos += hapd->conf->ssid.ssid_len;
+ if (!params->is_ml_sta_info) {
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = hapd->conf->ssid.ssid_len;
+ os_memcpy(pos, hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len);
+ pos += hapd->conf->ssid.ssid_len;
+ }
/* Supported rates */
pos = hostapd_eid_supp_rates(hapd, pos);
@@ -686,11 +714,18 @@
/* Power Constraint element */
pos = hostapd_eid_pwr_constraint(hapd, pos);
- /* CSA IE */
- csa_pos = hostapd_eid_csa(hapd, pos);
- if (csa_pos != pos)
- hapd->cs_c_off_proberesp = csa_pos - (u8 *) resp - 1;
- pos = csa_pos;
+ /*
+ * CSA IE
+ * TODO: This should be included inside the ML sta profile
+ */
+ if (!params->is_ml_sta_info) {
+ csa_pos = hostapd_eid_csa(hapd, pos);
+ if (csa_pos != pos)
+ params->csa_pos = csa_pos - 1;
+ else
+ params->csa_pos = NULL;
+ pos = csa_pos;
+ }
/* ERP Information element */
pos = hostapd_eid_erp_info(hapd, pos);
@@ -701,16 +736,23 @@
pos = hostapd_get_rsne(hapd, pos, epos - pos);
pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
pos = hostapd_eid_mbssid(hapd, pos, epos, WLAN_FC_STYPE_PROBE_RESP, 0,
- NULL, known_bss, known_bss_len, NULL, NULL,
- NULL, 0);
+ NULL, params->known_bss, params->known_bss_len,
+ NULL, NULL, NULL, 0);
pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
pos = hostapd_get_mde(hapd, pos, epos - pos);
- /* eCSA IE */
- csa_pos = hostapd_eid_ecsa(hapd, pos);
- if (csa_pos != pos)
- hapd->cs_c_off_ecsa_proberesp = csa_pos - (u8 *) resp - 1;
- pos = csa_pos;
+ /*
+ * eCSA IE
+ * TODO: This should be included inside the ML sta profile
+ */
+ if (!params->is_ml_sta_info) {
+ csa_pos = hostapd_eid_ecsa(hapd, pos);
+ if (csa_pos != pos)
+ params->ecsa_pos = csa_pos - 1;
+ else
+ params->ecsa_pos = NULL;
+ pos = csa_pos;
+ }
pos = hostapd_eid_supported_op_classes(hapd, pos);
pos = hostapd_eid_ht_capabilities(hapd, pos);
@@ -720,7 +762,7 @@
* when a list of known BSSes is included in the Probe Request frame. */
pos = hostapd_eid_ext_capab(hapd, pos,
hapd->iconf->mbssid >= MBSSID_ENABLED &&
- !known_bss_len);
+ !params->known_bss_len);
pos = hostapd_eid_time_adv(hapd, pos);
pos = hostapd_eid_time_zone(hapd, pos);
@@ -754,7 +796,8 @@
pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
- pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP);
+ if (!params->is_ml_sta_info)
+ pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP);
pos = hostapd_eid_fils_indic(hapd, pos, 0);
pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
@@ -768,7 +811,9 @@
/* BSS Color Change Announcement element */
cca_pos = hostapd_eid_cca(hapd, pos);
if (cca_pos != pos)
- hapd->cca_c_off_proberesp = cca_pos - (u8 *) resp - 2;
+ params->cca_pos = cca_pos - 2;
+ else
+ params->cca_pos = NULL;
pos = cca_pos;
pos = hostapd_eid_spatial_reuse(hapd, pos);
@@ -779,8 +824,14 @@
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
- if (hapd->conf->mld_ap)
- pos = hostapd_eid_eht_basic_ml(hapd, pos, NULL, true);
+ struct hostapd_data *ml_elem_ap =
+ params->mld_ap ? params->mld_ap : hapd;
+
+ if (ml_elem_ap->conf->mld_ap)
+ pos = hostapd_eid_eht_ml_beacon(
+ ml_elem_ap, params->mld_info,
+ pos, !!params->mld_ap);
+
pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP);
pos = hostapd_eid_eht_operation(hapd, pos);
}
@@ -807,7 +858,7 @@
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
- if ((hapd->conf->p2p & P2P_ENABLED) && is_p2p &&
+ if ((hapd->conf->p2p & P2P_ENABLED) && params->is_p2p &&
hapd->p2p_probe_resp_ie) {
os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie),
wpabuf_len(hapd->p2p_probe_resp_ie));
@@ -824,9 +875,9 @@
pos = hostapd_eid_hs20_indication(hapd, pos);
#endif /* CONFIG_HS20 */
- pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos);
- pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos);
- pos = hostapd_eid_dpp_cc(hapd, pos, (u8 *) resp + buflen - pos);
+ pos = hostapd_eid_mbo(hapd, pos, epos - pos);
+ pos = hostapd_eid_owe_trans(hapd, pos, epos - pos);
+ pos = hostapd_eid_dpp_cc(hapd, pos, epos - pos);
if (hapd->conf->vendor_elements) {
os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
@@ -834,11 +885,171 @@
pos += wpabuf_len(hapd->conf->vendor_elements);
}
- *resp_len = pos - (u8 *) resp;
- return (u8 *) resp;
+ return pos;
}
+static void hostapd_gen_probe_resp(struct hostapd_data *hapd,
+ struct probe_resp_params *params)
+{
+ u8 *pos;
+ size_t buflen;
+
+ hapd = hostapd_mbssid_get_tx_bss(hapd);
+
+#define MAX_PROBERESP_LEN 768
+ buflen = MAX_PROBERESP_LEN;
+ buflen += hostapd_probe_resp_elems_len(hapd, params);
+ params->resp = os_zalloc(buflen);
+ if (!params->resp) {
+ params->resp_len = 0;
+ return;
+ }
+
+ params->resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_PROBE_RESP);
+ /* Unicast the response to all requests on bands other than 6 GHz. For
+ * the 6 GHz, unicast is used only if the actual SSID is not included in
+ * the Beacon frames. Otherwise, broadcast response is used per IEEE
+ * Std 802.11ax-2021, 26.17.2.3.2. Broadcast address is also used for
+ * the Probe Response frame template for the unsolicited (i.e., not as
+ * a response to a specific request) case. */
+ if (params->req && (!is_6ghz_op_class(hapd->iconf->op_class) ||
+ hapd->conf->ignore_broadcast_ssid))
+ os_memcpy(params->resp->da, params->req->sa, ETH_ALEN);
+ else
+ os_memset(params->resp->da, 0xff, ETH_ALEN);
+ os_memcpy(params->resp->sa, hapd->own_addr, ETH_ALEN);
+
+ os_memcpy(params->resp->bssid, hapd->own_addr, ETH_ALEN);
+ params->resp->u.probe_resp.beacon_int =
+ host_to_le16(hapd->iconf->beacon_int);
+
+ /* hardware or low-level driver will setup seq_ctrl and timestamp */
+ params->resp->u.probe_resp.capab_info =
+ host_to_le16(hostapd_own_capab_info(hapd));
+
+ pos = hostapd_probe_resp_fill_elems(hapd, params,
+ params->resp->u.probe_resp.variable,
+ buflen);
+
+ params->resp_len = pos - (u8 *) params->resp;
+}
+
+
+#ifdef CONFIG_IEEE80211BE
+static void hostapd_fill_probe_resp_ml_params(struct hostapd_data *hapd,
+ struct probe_resp_params *params,
+ const struct ieee80211_mgmt *mgmt,
+ int mld_id, u16 links)
+{
+ 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));
+ if (!params->mld_info)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "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 : hapd->conf->mld_id;
+
+ for_each_mld_link(link, i, j, hapd->iface->interfaces,
+ probed_mld_id) {
+ struct mld_link_info *link_info;
+ size_t buflen;
+ u8 mld_link_id = link->mld_link_id;
+ u8 *epos;
+ u8 buf[EHT_ML_MAX_STA_PROF_LEN];
+
+ /*
+ * Set mld_ap iff the ML probe request explicitly
+ * requested a specific MLD ID. In that case, the targeted
+ * AP may have been a nontransmitted BSSID on the same
+ * interface.
+ */
+ if (mld_id != -1 && link->iface == hapd->iface)
+ params->mld_ap = link;
+
+ /* Never duplicate main Probe Response frame body */
+ if (link == hapd)
+ continue;
+
+ /* Only include requested links */
+ if (!(BIT(mld_link_id) & links))
+ continue;
+
+ link_info = ¶ms->mld_info->links[mld_link_id];
+
+ sta_info_params.req = params->req;
+ sta_info_params.is_p2p = false;
+ sta_info_params.is_ml_sta_info = true;
+ sta_info_params.mld_ap = NULL;
+ sta_info_params.mld_info = NULL;
+
+ buflen = MAX_PROBERESP_LEN;
+ buflen += hostapd_probe_resp_elems_len(link, &sta_info_params);
+
+ if (buflen > EHT_ML_MAX_STA_PROF_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Not including link %d in ML probe response (%zu bytes is too long)",
+ mld_link_id, buflen);
+ goto fail;
+ }
+
+ /*
+ * NOTE: This does not properly handle inheritance and
+ * various other things.
+ */
+ link_info->valid = true;
+ epos = buf;
+
+ /* Capabilities is the only fixed parameter */
+ WPA_PUT_LE16(epos, hostapd_own_capab_info(hapd));
+ epos += 2;
+
+ epos = hostapd_probe_resp_fill_elems(
+ link, &sta_info_params, epos,
+ EHT_ML_MAX_STA_PROF_LEN - 2);
+ link_info->resp_sta_profile_len = epos - buf;
+ os_free(link_info->resp_sta_profile);
+ link_info->resp_sta_profile = os_memdup(
+ buf, link_info->resp_sta_profile_len);
+ if (!link_info->resp_sta_profile)
+ link_info->resp_sta_profile_len = 0;
+ os_memcpy(link_info->local_addr, link->own_addr, ETH_ALEN);
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: ML probe response includes link sta info for %d: %u bytes (estimate %zu)",
+ mld_link_id, link_info->resp_sta_profile_len,
+ buflen);
+ }
+
+ if (mld_id != -1 && !params->mld_ap) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: No nontransmitted BSSID for MLD ID %d",
+ mld_id);
+ goto fail;
+ }
+
+ return;
+
+fail:
+ hostapd_free_probe_resp_params(params);
+ params->mld_ap = NULL;
+ params->mld_info = NULL;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
enum ssid_match_result {
NO_SSID_MATCH,
EXACT_SSID_MATCH,
@@ -953,7 +1164,7 @@
struct hostapd_sta_info *info;
dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list)
- if (os_memcmp(addr, info->addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(addr, info->addr))
return info;
return NULL;
@@ -1037,21 +1248,109 @@
#endif /* CONFIG_TAXONOMY */
+#ifdef CONFIG_IEEE80211BE
+static bool parse_ml_probe_req(const struct ieee80211_eht_ml *ml, size_t ml_len,
+ int *mld_id, u16 *links)
+{
+ u16 ml_control;
+ const struct element *sub;
+ const u8 *pos;
+ size_t len;
+
+ *mld_id = -1;
+ *links = 0xffff;
+
+ if (ml_len < sizeof(struct ieee80211_eht_ml))
+ return false;
+
+ ml_control = le_to_host16(ml->ml_control);
+ if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
+ MULTI_LINK_CONTROL_TYPE_PROBE_REQ) {
+ wpa_printf(MSG_DEBUG, "MLD: Not an ML probe req");
+ return false;
+ }
+
+ if (sizeof(struct ieee80211_eht_ml) + 1 > ml_len) {
+ wpa_printf(MSG_DEBUG, "MLD: ML probe req too short");
+ return false;
+ }
+
+ pos = ml->variable;
+ len = pos[0];
+ if (len < 1 || sizeof(struct ieee80211_eht_ml) + len > ml_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: ML probe request with invalid length");
+ return false;
+ }
+
+ if (ml_control & EHT_ML_PRES_BM_PROBE_REQ_AP_MLD_ID) {
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: ML probe req too short for MLD ID");
+ return false;
+ }
+
+ *mld_id = pos[1];
+ }
+ pos += len;
+
+ /* Parse subelements (if there are any) */
+ len = ml_len - len - sizeof(struct ieee80211_eht_ml);
+ for_each_element_id(sub, 0, pos, len) {
+ const struct ieee80211_eht_per_sta_profile *sta;
+ u16 sta_control;
+
+ if (*links == 0xffff)
+ *links = 0;
+
+ if (sub->datalen <
+ sizeof(struct ieee80211_eht_per_sta_profile)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: ML probe req %d too short for sta profile",
+ sub->datalen);
+ return false;
+ }
+
+ sta = (struct ieee80211_eht_per_sta_profile *) sub->data;
+
+ /*
+ * Extract the link ID, do not return whether a complete or
+ * partial profile was requested.
+ */
+ sta_control = le_to_host16(sta->sta_control);
+ *links |= BIT(sta_control & EHT_PER_STA_CTRL_LINK_ID_MSK);
+ }
+
+ if (!for_each_element_completed(sub, pos, len)) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: ML probe req sub-elements parsing error");
+ return false;
+ }
+
+ return true;
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
void handle_probe_req(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int ssi_signal)
{
- u8 *resp;
struct ieee802_11_elems elems;
const u8 *ie;
size_t ie_len;
- size_t i, resp_len;
+ size_t i;
int noack;
enum ssid_match_result res;
int ret;
u16 csa_offs[2];
size_t csa_offs_len;
struct radius_sta rad_info;
+ struct probe_resp_params params;
+#ifdef CONFIG_IEEE80211BE
+ int mld_id;
+ u16 links;
+#endif /* CONFIG_IEEE80211BE */
if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
ssi_signal < hapd->iconf->rssi_ignore_probe_request)
@@ -1217,7 +1516,7 @@
else
hessid = elems.interworking + 1 + 2;
if (!is_broadcast_ether_addr(hessid) &&
- os_memcmp(hessid, hapd->conf->hessid, ETH_ALEN) != 0) {
+ !ether_addr_equal(hessid, hapd->conf->hessid)) {
wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
" for mismatching HESSID " MACSTR
" ignored",
@@ -1283,10 +1582,28 @@
wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
" signal=%d", MAC2STR(mgmt->sa), ssi_signal);
- resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
- &resp_len, elems.mbssid_known_bss,
- elems.mbssid_known_bss_len);
- if (resp == NULL)
+ os_memset(¶ms, 0, sizeof(params));
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && elems.probe_req_mle &&
+ parse_ml_probe_req((struct ieee80211_eht_ml *) elems.probe_req_mle,
+ elems.probe_req_mle_len, &mld_id, &links)) {
+ hostapd_fill_probe_resp_ml_params(hapd, ¶ms, mgmt,
+ mld_id, links);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ params.req = mgmt;
+ params.is_p2p = !!elems.p2p;
+ params.known_bss = elems.mbssid_known_bss;
+ params.known_bss_len = elems.mbssid_known_bss_len;
+ params.is_ml_sta_info = false;
+
+ hostapd_gen_probe_resp(hapd, ¶ms);
+
+ hostapd_free_probe_resp_params(¶ms);
+
+ if (!params.resp)
return;
/*
@@ -1298,24 +1615,23 @@
csa_offs_len = 0;
if (hapd->csa_in_progress) {
- if (hapd->cs_c_off_proberesp)
+ if (params.csa_pos)
csa_offs[csa_offs_len++] =
- hapd->cs_c_off_proberesp;
+ params.csa_pos - (u8 *) params.resp;
- if (hapd->cs_c_off_ecsa_proberesp)
+ if (params.ecsa_pos)
csa_offs[csa_offs_len++] =
- hapd->cs_c_off_ecsa_proberesp;
+ params.ecsa_pos - (u8 *) params.resp;
}
- ret = hostapd_drv_send_mlme(hostapd_mbssid_get_tx_bss(hapd), resp,
- resp_len, noack,
+ ret = hostapd_drv_send_mlme(hapd, params.resp, params.resp_len, noack,
csa_offs_len ? csa_offs : NULL,
csa_offs_len, 0);
if (ret < 0)
wpa_printf(MSG_INFO, "handle_probe_req: send failed");
- os_free(resp);
+ os_free(params.resp);
wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s "
"SSID", MAC2STR(mgmt->sa),
@@ -1326,6 +1642,8 @@
static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd,
size_t *resp_len)
{
+ struct probe_resp_params params;
+
/* check probe response offloading caps and print warnings */
if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD))
return NULL;
@@ -1355,7 +1673,32 @@
"this");
/* Generate a Probe Response template for the non-P2P case */
- return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, NULL, 0);
+ os_memset(¶ms, 0, sizeof(params));
+ params.req = NULL;
+ params.is_p2p = false;
+ params.known_bss = NULL;
+ params.known_bss_len = 0;
+ params.is_ml_sta_info = false;
+ params.mld_ap = NULL;
+ params.mld_info = NULL;
+
+ hostapd_gen_probe_resp(hapd, ¶ms);
+ *resp_len = params.resp_len;
+ if (!params.resp)
+ return NULL;
+
+ /* TODO: Avoid passing these through struct hostapd_data */
+ if (params.csa_pos)
+ hapd->cs_c_off_proberesp = params.csa_pos - (u8 *) params.resp;
+ if (params.ecsa_pos)
+ hapd->cs_c_off_ecsa_proberesp = params.ecsa_pos -
+ (u8 *) params.resp;
+#ifdef CONFIG_IEEE80211AX
+ if (params.cca_pos)
+ hapd->cca_c_off_proberesp = params.cca_pos - (u8 *) params.resp;
+#endif /* CONFIG_IEEE80211AX */
+
+ return (u8 *) params.resp;
}
#endif /* NEED_AP_MLME */
@@ -1366,15 +1709,26 @@
static u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
struct wpa_driver_ap_params *params)
{
+ struct probe_resp_params probe_params;
+
if (!is_6ghz_op_class(hapd->iconf->op_class))
return NULL;
params->unsol_bcast_probe_resp_interval =
hapd->conf->unsol_bcast_probe_resp_interval;
- return hostapd_gen_probe_resp(hapd, NULL, 0,
- ¶ms->unsol_bcast_probe_resp_tmpl_len,
- NULL, 0);
+ os_memset(&probe_params, 0, sizeof(probe_params));
+ probe_params.req = NULL;
+ probe_params.is_p2p = false;
+ probe_params.known_bss = NULL;
+ probe_params.known_bss_len = 0;
+ probe_params.is_ml_sta_info = false;
+ probe_params.mld_ap = NULL;
+ probe_params.mld_info = NULL;
+
+ hostapd_gen_probe_resp(hapd, &probe_params);
+ params->unsol_bcast_probe_resp_tmpl_len = probe_params.resp_len;
+ return (u8 *) probe_params.resp;
}
#endif /* CONFIG_IEEE80211AX */
@@ -1606,14 +1960,9 @@
buf_len = pos - buf;
total_len += buf_len;
-#ifdef CONFIG_IEEE80211AX
- /* Transmit Power Envelope element(s) */
- if (is_6ghz_op_class(hapd->iconf->op_class)) {
- total_len += 4;
- if (hapd->iconf->he_6ghz_reg_pwr_type == HE_6GHZ_INDOOR_AP)
- total_len += 4;
- }
-#endif /* CONFIG_IEEE80211AX */
+ /* he_elem_len() may return too large a value for FD frame, but that is
+ * fine here since this is used as the maximum length of the buffer. */
+ total_len += he_elem_len(hapd);
head = os_zalloc(total_len);
if (!head)
@@ -1763,23 +2112,7 @@
}
#endif /* CONFIG_IEEE80211AC */
-#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
- tail_len += 3 + sizeof(struct ieee80211_he_capabilities) +
- 3 + sizeof(struct ieee80211_he_operation) +
- 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
- 3 + sizeof(struct ieee80211_spatial_reuse);
- if (is_6ghz_op_class(hapd->iconf->op_class)) {
- tail_len += sizeof(struct ieee80211_he_6ghz_oper_info) +
- 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
- /* An additional Transmit Power Envelope element for
- * subordinate client */
- if (hapd->iconf->he_6ghz_reg_pwr_type ==
- HE_6GHZ_INDOOR_AP)
- tail_len += 4;
- }
- }
-#endif /* CONFIG_IEEE80211AX */
+ tail_len += he_elem_len(hapd);
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
@@ -1966,8 +2299,8 @@
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
if (hapd->conf->mld_ap)
- tailpos = hostapd_eid_eht_basic_ml(hapd, tailpos, NULL,
- true);
+ tailpos = hostapd_eid_eht_ml_beacon(hapd, NULL,
+ tailpos, false);
tailpos = hostapd_eid_eht_capab(hapd, tailpos,
IEEE80211_MODE_AP);
tailpos = hostapd_eid_eht_operation(hapd, tailpos);
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index a6fcb7e..5378671 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -830,6 +830,17 @@
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
+
+ if (is_6ghz_op_class(iface->conf->op_class) &&
+ hostapd_get_oper_chwidth(iface->conf) ==
+ CONF_OPER_CHWIDTH_320MHZ) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "eht_bw320_offset=%d\n",
+ iface->conf->eht_bw320_offset);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
}
#endif /* CONFIG_IEEE80211BE */
@@ -1094,7 +1105,7 @@
return -1;
return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
- pmkid, expiration, akmp);
+ pmkid, expiration, akmp, NULL);
}
@@ -1315,6 +1326,8 @@
req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
if (os_strstr(cmd, " disassoc_imminent=1"))
req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+ if (os_strstr(cmd, " link_removal_imminent=1"))
+ req_mode |= WNM_BSS_TM_REQ_LINK_REMOVAL_IMMINENT;
#ifdef CONFIG_MBO
pos = os_strstr(cmd, "mbo=");
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 9a5d3c8..5e4c810 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -188,7 +188,7 @@
* If it's not allowed to use the first channel as primary, decline the
* whole channel range. */
if (!chan_pri_allowed(first_chan)) {
- wpa_printf(MSG_DEBUG, "DFS: primary chanenl not allowed");
+ wpa_printf(MSG_DEBUG, "DFS: primary channel not allowed");
return 0;
}
@@ -551,6 +551,8 @@
if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
return NULL;
chan_idx = _rand % num_available_chandefs;
+ wpa_printf(MSG_DEBUG, "DFS: Picked random entry from the list: %d/%d",
+ chan_idx, num_available_chandefs);
dfs_find_channel(iface, &chan, chan_idx, type);
if (!chan) {
wpa_printf(MSG_DEBUG, "DFS: no random channel found");
@@ -983,6 +985,11 @@
os_memset(&csa_settings, 0, sizeof(csa_settings));
csa_settings.cs_count = 5;
csa_settings.block_tx = 1;
+ csa_settings.link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (iface->bss[0]->conf->mld_ap)
+ csa_settings.link_id = iface->bss[0]->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_MESH
if (iface->mconf)
ieee80211_mode = IEEE80211_MODE_MESH;
@@ -1044,7 +1051,7 @@
}
-static void hostpad_dfs_update_background_chain(struct hostapd_iface *iface)
+static void hostapd_dfs_update_background_chain(struct hostapd_iface *iface)
{
int sec = 0;
enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
@@ -1119,7 +1126,7 @@
hostapd_set_oper_centr_freq_seg1_idx(
iface->conf, iface->radar_background.centr_freq_seg1_idx);
- hostpad_dfs_update_background_chain(iface);
+ hostapd_dfs_update_background_chain(iface);
return hostapd_dfs_request_channel_switch(
iface, iface->conf->channel, iface->freq,
@@ -1183,7 +1190,7 @@
}
} else if (hostapd_dfs_is_background_event(iface, freq)) {
iface->radar_background.cac_started = 0;
- hostpad_dfs_update_background_chain(iface);
+ hostapd_dfs_update_background_chain(iface);
}
return 0;
@@ -1317,7 +1324,7 @@
* Just select a new random channel according to the
* regulations for monitoring.
*/
- hostpad_dfs_update_background_chain(iface);
+ hostapd_dfs_update_background_chain(iface);
return 0;
}
@@ -1479,7 +1486,7 @@
} else if (dfs_use_radar_background(iface) &&
iface->radar_background.channel == -1) {
/* Reset radar background chain if disabled */
- hostpad_dfs_update_background_chain(iface);
+ hostapd_dfs_update_background_chain(iface);
}
return 0;
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 7a8ea4e..3f89bc2 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -539,8 +539,15 @@
return;
}
- if (hapd->dpp_auth_ok_on_ack)
+ if (hapd->dpp_auth_ok_on_ack) {
hostapd_dpp_auth_success(hapd, 1);
+ if (!hapd->dpp_auth) {
+ /* The authentication session could have been removed in
+ * some error cases, e.g., when starting GAS client and
+ * failing to send the initial request. */
+ return;
+ }
+ }
if (!is_broadcast_ether_addr(dst) && !ok) {
wpa_printf(MSG_DEBUG,
@@ -1413,7 +1420,7 @@
}
if (!is_zero_ether_addr(auth->peer_mac_addr) &&
- os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -1463,7 +1470,7 @@
return;
}
- if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -1572,7 +1579,7 @@
return;
}
- if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -1858,7 +1865,7 @@
return;
}
- if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(src, auth->peer_mac_addr)) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
@@ -2133,7 +2140,7 @@
else
expiration = 0;
- if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
intro.pmkid, expiration,
WPA_KEY_MGMT_DPP, pkhash) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
@@ -2907,7 +2914,7 @@
else
expiration = 0;
- if (wpa_auth_pmksa_add3(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
intro.pmkid, expiration,
WPA_KEY_MGMT_DPP, pkhash) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
@@ -3073,7 +3080,7 @@
wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
- os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(sa, auth->peer_mac_addr)) {
#ifdef CONFIG_DPP2
if (dpp_relay_rx_gas_req(hapd->iface->interfaces->dpp, sa, data,
data_len) == 0) {
@@ -3094,6 +3101,13 @@
* exchange. */
dpp_notify_auth_success(hapd->dpp_auth, 1);
hapd->dpp_auth_ok_on_ack = 0;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
}
wpa_hexdump(MSG_DEBUG,
@@ -3948,11 +3962,25 @@
ifaces->dpp_pb_time.usec = 0;
dpp_pkex_free(hapd->dpp_pkex);
hapd->dpp_pkex = NULL;
+ hapd->dpp_pkex_bi = NULL;
os_free(hapd->dpp_pkex_auth_cmd);
hapd->dpp_pkex_auth_cmd = NULL;
if (ifaces->dpp_pb_bi) {
char id[20];
+ size_t i;
+
+ for (i = 0; i < ifaces->count; i++) {
+ struct hostapd_iface *iface = ifaces->iface[i];
+ size_t j;
+
+ for (j = 0; iface && j < iface->num_bss; j++) {
+ struct hostapd_data *h = iface->bss[j];
+
+ if (h->dpp_pkex_bi == ifaces->dpp_pb_bi)
+ h->dpp_pkex_bi = NULL;
+ }
+ }
os_snprintf(id, sizeof(id), "%u", ifaces->dpp_pb_bi->id);
dpp_bootstrap_remove(ifaces->dpp, id);
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 98794c2..533cc54 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -42,6 +42,7 @@
#include "dpp_hostapd.h"
#include "fils_hlp.h"
#include "neighbor_db.h"
+#include "nan_usd_ap.h"
#ifdef CONFIG_FILS
@@ -52,6 +53,7 @@
struct ieee802_11_elems elems;
u8 buf[IEEE80211_MAX_MMPDU_SIZE], *p = buf;
int new_assoc;
+ bool updated;
wpa_printf(MSG_DEBUG, "%s FILS: Finish association with " MACSTR,
__func__, MAC2STR(sta->addr));
@@ -76,11 +78,13 @@
sta->fils_pending_assoc_is_reassoc,
WLAN_STATUS_SUCCESS,
buf, p - buf);
- ap_sta_set_authorized(hapd, sta, 1);
+ updated = ap_sta_set_authorized_flag(hapd, sta, 1);
new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
hostapd_set_sta_flags(hapd, sta);
+ if (updated)
+ ap_sta_set_authorized_event(hapd, sta, 1);
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS);
ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
hostapd_new_assoc_sta(hapd, sta, !new_assoc);
@@ -158,7 +162,7 @@
return -1;
}
- mlebuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ mlebuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len, true);
if (!mlebuf) {
wpa_printf(MSG_ERROR,
"MLO: Basic Multi-Link element not found in (Re)Association Response frame");
@@ -263,6 +267,7 @@
#ifdef CONFIG_OWE
struct hostapd_iface *iface = hapd->iface;
#endif /* CONFIG_OWE */
+ bool updated = false;
if (addr == NULL) {
/*
@@ -279,7 +284,7 @@
if (is_multicast_ether_addr(addr) ||
is_zero_ether_addr(addr) ||
- os_memcmp(addr, hapd->own_addr, ETH_ALEN) == 0) {
+ ether_addr_equal(addr, hapd->own_addr)) {
/* Do not process any frames with unexpected/invalid SA so that
* we do not add any state for unexpected STA addresses or end
* up sending out frames to unexpected destination. */
@@ -358,7 +363,7 @@
int i, num_valid_links = 0;
u8 link_id = hapd->mld_link_id;
- info->mld_sta = true;
+ ap_sta_set_mld(sta, true);
sta->mld_assoc_link_id = link_id;
os_memcpy(info->common_info.mld_addr, addr, ETH_ALEN);
info->links[link_id].valid = true;
@@ -509,7 +514,7 @@
return -1;
}
#ifdef CONFIG_IEEE80211BE
- if (sta->mld_info.mld_sta) {
+ 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_addr,
@@ -845,18 +850,30 @@
sta->auth_alg == WLAN_AUTH_FILS_SK ||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK)
- ap_sta_set_authorized(hapd, sta, 1);
+ updated = ap_sta_set_authorized_flag(hapd, sta, 1);
#else /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
/* Keep compiler silent about unused variables */
if (status) {
}
#endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */
+#ifdef CONFIG_IEEE80211BE
+ if (hostapd_process_assoc_ml_info(hapd, sta, req_ies, req_ies_len,
+ !!reassoc, WLAN_STATUS_SUCCESS,
+ true)) {
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ reason = WLAN_REASON_UNSPECIFIED;
+ goto fail;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
hostapd_set_sta_flags(hapd, sta);
+ if (updated)
+ ap_sta_set_authorized_event(hapd, sta, 1);
if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
@@ -1164,6 +1181,8 @@
hostapd_set_oper_chwidth(hapd->iconf, chwidth);
hostapd_set_oper_centr_freq_seg0_idx(hapd->iconf, seg0_idx);
hostapd_set_oper_centr_freq_seg1_idx(hapd->iconf, seg1_idx);
+ /* Auto-detect new bw320_offset */
+ hostapd_set_and_check_bw320_offset(hapd->iconf, 0);
#ifdef CONFIG_IEEE80211BE
hapd->iconf->punct_bitmap = punct_bitmap;
#endif /* CONFIG_IEEE80211BE */
@@ -1270,6 +1289,18 @@
int err = 0;
struct hostapd_channel_data *pri_chan;
+#ifdef CONFIG_IEEE80211BE
+ if (acs_res->link_id != -1) {
+ hapd = hostapd_mld_get_link_bss(hapd, acs_res->link_id);
+ if (!hapd) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Failed to get link BSS for EVENT_ACS_CHANNEL_SELECTED link_id=%d",
+ acs_res->link_id);
+ return;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
if (hapd->iconf->channel) {
wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
hapd->iconf->channel);
@@ -1576,6 +1607,7 @@
#endif /* CONFIG_FST */
#ifdef CONFIG_DPP
if (plen >= 2 + 4 &&
+ mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
mgmt->u.action.u.vs_public_action.action ==
WLAN_PA_VENDOR_SPECIFIC &&
WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
@@ -1591,6 +1623,23 @@
return;
}
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ if (mgmt->u.action.category == WLAN_ACTION_PUBLIC && plen >= 5 &&
+ mgmt->u.action.u.vs_public_action.action ==
+ WLAN_PA_VENDOR_SPECIFIC &&
+ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+ OUI_WFA &&
+ mgmt->u.action.u.vs_public_action.variable[0] == NAN_OUI_TYPE) {
+ const u8 *pos, *end;
+
+ pos = mgmt->u.action.u.vs_public_action.variable;
+ end = drv_mgmt->frame + drv_mgmt->frame_len;
+ pos++;
+ hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, drv_mgmt->freq,
+ pos, end - pos);
+ return;
+ }
+#endif /* CONFIG_NAN_USD */
}
#endif /* NEED_AP_MLME */
@@ -1628,7 +1677,7 @@
return HAPD_BROADCAST;
for (i = 0; i < iface->num_bss; i++) {
- if (os_memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(bssid, iface->bss[i]->own_addr))
return iface->bss[i];
}
@@ -1682,7 +1731,7 @@
#ifdef CONFIG_IEEE80211BE
if (hapd->conf->mld_ap &&
- os_memcmp(hapd->mld_addr, bssid, ETH_ALEN) == 0)
+ ether_addr_equal(hapd->mld_addr, bssid))
is_mld = true;
#endif /* CONFIG_IEEE80211BE */
@@ -1754,8 +1803,7 @@
hapd = tmp_hapd;
#ifdef CONFIG_IEEE80211BE
} else if (hapd->conf->mld_ap &&
- os_memcmp(hapd->mld_addr, get_hdr_bssid(hdr, len),
- ETH_ALEN) == 0) {
+ ether_addr_equal(hapd->mld_addr, get_hdr_bssid(hdr, len))) {
/* AP MLD address match - use hapd pointer as-is */
#endif /* CONFIG_IEEE80211BE */
} else {
@@ -1803,14 +1851,15 @@
static struct hostapd_data * hostapd_find_by_sta(struct hostapd_iface *iface,
- const u8 *src)
+ const u8 *src, bool rsn)
{
struct sta_info *sta;
unsigned int j;
for (j = 0; j < iface->num_bss; j++) {
sta = ap_get_sta(iface->bss[j], src);
- if (sta && sta->flags & WLAN_STA_ASSOC)
+ if (sta && (sta->flags & WLAN_STA_ASSOC) &&
+ (!rsn || sta->wpa_sm))
return iface->bss[j];
}
@@ -1818,6 +1867,40 @@
}
+#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];
+ struct hostapd_bss_config *hconf = h_hapd->conf;
+
+ if (!hconf->mld_ap ||
+ hconf->mld_id != hapd->conf->mld_id)
+ 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,
@@ -1830,36 +1913,24 @@
struct hostapd_data *h_hapd;
hapd = switch_link_hapd(hapd, link_id);
- h_hapd = hostapd_find_by_sta(hapd->iface, src);
+ h_hapd = hostapd_find_by_sta(hapd->iface, src, true);
if (!h_hapd)
- h_hapd = hostapd_find_by_sta(orig_hapd->iface, src);
+ 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) {
- 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];
- struct hostapd_bss_config *hconf = h_hapd->conf;
-
- if (!hconf->mld_ap ||
- hconf->mld_id != hapd->conf->mld_id)
- continue;
-
- h_hapd = hostapd_find_by_sta(h, src);
- if (h_hapd) {
- hapd = h_hapd;
- break;
- }
- }
+ search_mld_sta(&hapd, src);
} else {
- hapd = hostapd_find_by_sta(hapd->iface, src);
+ hapd = hostapd_find_by_sta(hapd->iface, src, false);
}
#else /* CONFIG_IEEE80211BE */
- hapd = hostapd_find_by_sta(hapd->iface, src);
+ hapd = hostapd_find_by_sta(hapd->iface, src, false);
#endif /* CONFIG_IEEE80211BE */
if (!hapd) {
@@ -2164,8 +2235,8 @@
struct mld_info *info = &sta->mld_info;
u8 link_id = hapd->mld_link_id;
- info->mld_sta = true;
- sta->mld_assoc_link_id = link_id;;
+ ap_sta_set_mld(sta, true);
+ sta->mld_assoc_link_id = link_id;
os_memcpy(info->common_info.mld_addr, peer, ETH_ALEN);
info->links[link_id].valid = true;
os_memcpy(info->links[link_id].local_addr, hapd->own_addr,
@@ -2364,6 +2435,18 @@
case EVENT_CH_SWITCH:
if (!data)
break;
+#ifdef CONFIG_IEEE80211BE
+ if (data->ch_switch.link_id != -1) {
+ hapd = hostapd_mld_get_link_bss(
+ hapd, data->ch_switch.link_id);
+ if (!hapd) {
+ wpa_printf(MSG_ERROR,
+ "MLD: Failed to get link (ID %d) BSS for EVENT_CH_SWITCH/EVENT_CH_SWITCH_STARTED",
+ data->ch_switch.link_id);
+ break;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
hostapd_event_ch_switch(hapd, data->ch_switch.freq,
data->ch_switch.ht_enabled,
data->ch_switch.ch_offset,
@@ -2390,26 +2473,31 @@
case EVENT_DFS_RADAR_DETECTED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
break;
case EVENT_DFS_PRE_CAC_EXPIRED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_pre_cac_expired(hapd, &data->dfs_event);
break;
case EVENT_DFS_CAC_FINISHED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_cac_finished(hapd, &data->dfs_event);
break;
case EVENT_DFS_CAC_ABORTED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event);
break;
case EVENT_DFS_NOP_FINISHED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
break;
case EVENT_CHANNEL_LIST_CHANGED:
@@ -2423,6 +2511,7 @@
case EVENT_DFS_CAC_STARTED:
if (!data)
break;
+ hapd = switch_link_hapd(hapd, data->dfs_event.link_id);
hostapd_event_dfs_cac_started(hapd, &data->dfs_event);
break;
#endif /* NEED_AP_MLME */
diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c
index d64fb8c..a34b5ba 100644
--- a/src/ap/fils_hlp.c
+++ b/src/ap/fils_hlp.c
@@ -546,7 +546,7 @@
" src=" MACSTR " len=%u)",
MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN),
(unsigned int) len);
- if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(sta->addr, pos + ETH_ALEN)) {
wpa_printf(MSG_DEBUG,
"FILS: Ignore HLP request with unexpected source address"
MACSTR, MAC2STR(pos + ETH_ALEN));
diff --git a/src/ap/gas_query_ap.c b/src/ap/gas_query_ap.c
index 3d94407..a471c79 100644
--- a/src/ap/gas_query_ap.c
+++ b/src/ap/gas_query_ap.c
@@ -185,7 +185,7 @@
{
struct gas_query_pending *q;
dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
- if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(q->addr, addr) &&
q->dialog_token == dialog_token)
return q;
}
@@ -223,7 +223,7 @@
wpa_printf(MSG_DEBUG, "GAS: TX status: dst=" MACSTR
" ok=%d query=%p dialog_token=%u dur=%d ms",
MAC2STR(dst), ok, query, query->dialog_token, dur);
- if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(dst, query->addr)) {
wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
return;
}
@@ -618,7 +618,7 @@
{
struct gas_query_pending *q;
dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
- if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(dst, q->addr) &&
dialog_token == q->dialog_token)
return 0;
}
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 236381f..ddbcabc 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -35,6 +35,7 @@
#include "wpa_auth.h"
#include "wps_hostapd.h"
#include "dpp_hostapd.h"
+#include "nan_usd_ap.h"
#include "gas_query_ap.h"
#include "hw_features.h"
#include "wpa_auth_glue.h"
@@ -413,6 +414,61 @@
}
+#ifdef CONFIG_IEEE80211BE
+#ifdef CONFIG_TESTING_OPTIONS
+
+#define TU_TO_USEC(_val) ((_val) * 1024)
+
+static void hostapd_link_remove_timeout_handler(void *eloop_data,
+ void *user_ctx)
+{
+ struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
+
+ if (hapd->eht_mld_link_removal_count == 0)
+ return;
+ hapd->eht_mld_link_removal_count--;
+
+ wpa_printf(MSG_DEBUG, "MLD: Remove link_id=%u in %u beacons",
+ hapd->mld_link_id,
+ hapd->eht_mld_link_removal_count);
+
+ ieee802_11_set_beacon(hapd);
+
+ if (!hapd->eht_mld_link_removal_count) {
+ hostapd_disable_iface(hapd->iface);
+ return;
+ }
+
+ eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int),
+ hostapd_link_remove_timeout_handler,
+ hapd, NULL);
+}
+
+
+int hostapd_link_remove(struct hostapd_data *hapd, u32 count)
+{
+ if (!hapd->conf->mld_ap)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "MLD: Remove link_id=%u in %u beacons",
+ hapd->mld_link_id, count);
+
+ hapd->eht_mld_link_removal_count = count;
+ hapd->eht_mld_bss_param_change++;
+
+ eloop_register_timeout(0, TU_TO_USEC(hapd->iconf->beacon_int),
+ hostapd_link_remove_timeout_handler,
+ hapd, NULL);
+
+ ieee802_11_set_beacon(hapd);
+ return 0;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_IEEE80211BE */
+
+
void hostapd_free_hapd_data(struct hostapd_data *hapd)
{
os_free(hapd->probereq_cb);
@@ -441,6 +497,24 @@
hostapd_acl_deinit(hapd);
#ifndef CONFIG_NO_RADIUS
if (!hapd->mld_first_bss) {
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+ size_t i;
+
+ for (i = 0; i < ifaces->count; i++) {
+ struct hostapd_iface *iface = ifaces->iface[i];
+ size_t j;
+
+ for (j = 0; iface && j < iface->num_bss; j++) {
+ struct hostapd_data *h = iface->bss[j];
+
+ if (hapd == h)
+ continue;
+ if (h->radius == hapd->radius)
+ h->radius = NULL;
+ if (h->radius_das == hapd->radius_das)
+ h->radius_das = NULL;
+ }
+ }
radius_client_deinit(hapd->radius);
radius_das_deinit(hapd->radius_das);
}
@@ -455,6 +529,9 @@
gas_query_ap_deinit(hapd->gas);
hapd->gas = NULL;
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ hostapd_nan_usd_deinit(hapd);
+#endif /* CONFIG_NAN_USD */
authsrv_deinit(hapd);
@@ -502,7 +579,9 @@
hapd->setup_complete_cb = NULL;
#endif /* CONFIG_MESH */
+#ifndef CONFIG_NO_RRM
hostapd_clean_rrm(hapd);
+#endif /* CONFIG_NO_RRM */
fils_hlp_deinit(hapd);
#ifdef CONFIG_OCV
@@ -525,6 +604,12 @@
#ifdef CONFIG_IEEE80211AX
eloop_cancel_timeout(hostapd_switch_color_timeout_handler, hapd, NULL);
+#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_IEEE80211BE
+ eloop_cancel_timeout(hostapd_link_remove_timeout_handler, hapd, NULL);
+#endif /* CONFIG_IEEE80211BE */
+#endif /* CONFIG_TESTING_OPTIONS */
+
#endif /* CONFIG_IEEE80211AX */
}
@@ -1435,6 +1520,11 @@
return -1;
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ if (hostapd_nan_usd_init(hapd) < 0)
+ return -1;
+#endif /* CONFIG_NAN_USD */
+
if (authsrv_init(hapd) < 0)
return -1;
@@ -3117,6 +3207,31 @@
return -1;
}
+#ifdef CONFIG_IEEE80211BE
+ if (hapd_iface->bss[0]->conf->mld_ap &&
+ !hapd_iface->bss[0]->mld_first_bss) {
+ /* Do not allow mld_first_bss disabling before other BSSs */
+ for (j = 0; j < hapd_iface->interfaces->count; ++j) {
+ struct hostapd_iface *h_iface =
+ hapd_iface->interfaces->iface[j];
+ struct hostapd_data *h_hapd = h_iface->bss[0];
+ struct hostapd_bss_config *h_conf = h_hapd->conf;
+
+ if (!h_conf->mld_ap ||
+ h_conf->mld_id !=
+ hapd_iface->bss[0]->conf->mld_id ||
+ h_iface == hapd_iface)
+ continue;
+
+ if (h_iface->state != HAPD_IFACE_DISABLED) {
+ wpa_printf(MSG_INFO,
+ "Do not allow disable mld_first_bss first");
+ return -1;
+ }
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
driver = hapd_iface->bss[0]->driver;
drv_priv = hapd_iface->bss[0]->drv_priv;
@@ -3536,7 +3651,7 @@
}
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta &&
+ if (ap_sta_is_mld(hapd, sta) &&
sta->mld_assoc_link_id != hapd->mld_link_id)
return;
#endif /* CONFIG_IEEE80211BE */
@@ -3768,7 +3883,7 @@
struct hostapd_freq_params *old_params)
{
int channel;
- u8 seg0, seg1;
+ u8 seg0 = 0, seg1 = 0;
struct hostapd_hw_modes *mode;
if (!params->channel) {
@@ -3844,10 +3959,14 @@
conf->ieee80211n = params->ht_enabled;
conf->ieee80211ac = params->vht_enabled;
conf->secondary_channel = params->sec_channel_offset;
- ieee80211_freq_to_chan(params->center_freq1,
- &seg0);
- ieee80211_freq_to_chan(params->center_freq2,
- &seg1);
+ if (params->center_freq1 &&
+ ieee80211_freq_to_chan(params->center_freq1, &seg0) ==
+ NUM_HOSTAPD_MODES)
+ return -1;
+ if (params->center_freq2 &&
+ ieee80211_freq_to_chan(params->center_freq2,
+ &seg1) == NUM_HOSTAPD_MODES)
+ return -1;
hostapd_set_oper_centr_freq_seg0_idx(conf, seg0);
hostapd_set_oper_centr_freq_seg1_idx(conf, seg1);
@@ -3945,6 +4064,11 @@
settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp;
settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon;
settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp;
+ settings->link_id = -1;
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap)
+ settings->link_id = hapd->mld_link_id;
+#endif /* CONFIG_IEEE80211BE */
return 0;
}
@@ -4040,13 +4164,17 @@
bw = CONF_OPER_CHWIDTH_USE_HT;
break;
case 80:
- if (freq_params->center_freq2)
+ if (freq_params->center_freq2) {
bw = CONF_OPER_CHWIDTH_80P80MHZ;
- else
+ iface->conf->vht_capab |=
+ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ } else {
bw = CONF_OPER_CHWIDTH_80MHZ;
+ }
break;
case 160:
bw = CONF_OPER_CHWIDTH_160MHZ;
+ iface->conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
break;
case 320:
bw = CONF_OPER_CHWIDTH_320MHZ;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 7f703be..bcf980f 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -469,6 +469,17 @@
#ifdef CONFIG_CTRL_IFACE_UDP
unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
#endif /* CONFIG_CTRL_IFACE_UDP */
+
+#ifdef CONFIG_IEEE80211BE
+ u8 eht_mld_bss_param_change;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 eht_mld_link_removal_count;
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_IEEE80211BE */
+
+#ifdef CONFIG_NAN_USD
+ struct nan_de *nan_de;
+#endif /* CONFIG_NAN_USD */
};
@@ -771,5 +782,25 @@
int hostapd_mbssid_get_bss_index(struct hostapd_data *hapd);
struct hostapd_data * hostapd_mld_get_link_bss(struct hostapd_data *hapd,
u8 link_id);
+int hostapd_link_remove(struct hostapd_data *hapd, u32 count);
+
+#ifdef CONFIG_IEEE80211BE
+#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 && \
+ _link->conf->mld_id == _mld_id; \
+ _link = NULL)
+#else /* CONFIG_IEEE80211BE */
+#define for_each_mld_link(_link, _bss_idx, _iface_idx, _ifaces, _mld_id) \
+ if (false)
+#endif /* CONFIG_IEEE80211BE */
#endif /* HOSTAPD_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 9edbb5a..596f2f0 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -1001,7 +1001,7 @@
{
int secondary_freq;
struct hostapd_channel_data *pri_chan;
- int err;
+ int err, err2;
if (!iface->current_mode)
return 0;
@@ -1044,15 +1044,15 @@
/* Both HT40+ and HT40- are set, pick a valid secondary channel */
secondary_freq = iface->freq + 20;
- err = hostapd_is_usable_chan(iface, secondary_freq, 0);
- if (err > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
+ err2 = hostapd_is_usable_chan(iface, secondary_freq, 0);
+ if (err2 > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
iface->conf->secondary_channel = 1;
return 1;
}
secondary_freq = iface->freq - 20;
- err = hostapd_is_usable_chan(iface, secondary_freq, 0);
- if (err > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
+ err2 = hostapd_is_usable_chan(iface, secondary_freq, 0);
+ if (err2 > 0 && (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
iface->conf->secondary_channel = -1;
return 1;
}
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 1f39107..8b8c1f0 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -56,6 +56,7 @@
#include "dpp_hostapd.h"
#include "gas_query_ap.h"
#include "comeback_token.h"
+#include "nan_usd_ap.h"
#include "pasn/pasn_common.h"
@@ -407,7 +408,7 @@
* the MLD MAC address. Thus, use the MLD address instead of translating
* the addresses.
*/
- if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
sa = hapd->mld_addr;
ml_resp = hostapd_ml_auth_resp(hapd);
@@ -556,7 +557,7 @@
for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
if (!is_broadcast_ether_addr(pw->peer_addr) &&
(!sta ||
- os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0))
+ !ether_addr_equal(pw->peer_addr, sta->addr)))
continue;
if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
continue;
@@ -608,7 +609,7 @@
const u8 *own_addr = hapd->own_addr;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta)
+ if (ap_sta_is_mld(hapd, sta))
own_addr = hapd->mld_addr;
#endif /* CONFIG_IEEE80211BE */
@@ -877,7 +878,7 @@
params.status = status;
#ifdef CONFIG_IEEE80211BE
- if (sta->mld_info.mld_sta)
+ if (ap_sta_is_mld(hapd, sta))
params.bssid =
sta->mld_info.links[sta->mld_assoc_link_id].peer_addr;
#endif /* CONFIG_IEEE80211BE */
@@ -902,23 +903,27 @@
" to VLAN ID %d",
MAC2STR(sta->addr), sta->sae->tmp->vlan_id);
- os_memset(&vlan_desc, 0, sizeof(vlan_desc));
- vlan_desc.notempty = 1;
- vlan_desc.untagged = sta->sae->tmp->vlan_id;
- if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
- wpa_printf(MSG_INFO,
- "Invalid VLAN ID %d in sae_password",
- sta->sae->tmp->vlan_id);
- return;
- }
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
+ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+ vlan_desc.notempty = 1;
+ vlan_desc.untagged = sta->sae->tmp->vlan_id;
+ if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
+ wpa_printf(MSG_INFO,
+ "Invalid VLAN ID %d in sae_password",
+ sta->sae->tmp->vlan_id);
+ return;
+ }
- if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 ||
- ap_sta_bind_vlan(hapd, sta) < 0) {
- wpa_printf(MSG_INFO,
- "Failed to assign VLAN ID %d from sae_password to "
- MACSTR, sta->sae->tmp->vlan_id,
- MAC2STR(sta->addr));
- return;
+ if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 ||
+ ap_sta_bind_vlan(hapd, sta) < 0) {
+ wpa_printf(MSG_INFO,
+ "Failed to assign VLAN ID %d from sae_password to "
+ MACSTR, sta->sae->tmp->vlan_id,
+ MAC2STR(sta->addr));
+ return;
+ }
+ } else {
+ sta->vlan_id = sta->sae->tmp->vlan_id;
}
}
#endif /* CONFIG_NO_VLAN */
@@ -1273,7 +1278,8 @@
pos = mgmt->u.auth.variable;
end = ((const u8 *) mgmt) + len;
resp = status_code;
- send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ send_auth_reply(hapd, sta, sta->addr, mgmt->bssid,
+ WLAN_AUTH_SAE,
auth_transaction, resp, pos, end - pos,
"auth-sae-reflection-attack");
goto remove_sta;
@@ -1281,7 +1287,8 @@
if (hapd->conf->sae_commit_override && auth_transaction == 1) {
wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
- send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ send_auth_reply(hapd, sta, sta->addr, mgmt->bssid,
+ WLAN_AUTH_SAE,
auth_transaction, resp,
wpabuf_head(hapd->conf->sae_commit_override),
wpabuf_len(hapd->conf->sae_commit_override),
@@ -1552,7 +1559,8 @@
data = wpabuf_alloc_copy(pos, 2);
sae_sme_send_external_auth_status(hapd, sta, resp);
- send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ send_auth_reply(hapd, sta, sta->addr, mgmt->bssid,
+ WLAN_AUTH_SAE,
auth_transaction, resp,
data ? wpabuf_head(data) : (u8 *) "",
data ? wpabuf_len(data) : 0, "auth-sae");
@@ -1664,7 +1672,7 @@
dl_list_for_each(q2, &hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list) {
mgmt2 = (const struct ieee80211_mgmt *) q2->msg;
- if (os_memcmp(mgmt->sa, mgmt2->sa, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(mgmt->sa, mgmt2->sa) &&
mgmt->u.auth.auth_transaction ==
mgmt2->u.auth.auth_transaction) {
wpa_printf(MSG_DEBUG,
@@ -1695,7 +1703,7 @@
dl_list_for_each(q, &hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list) {
mgmt = (const struct ieee80211_mgmt *) q->msg;
- if (os_memcmp(addr, mgmt->sa, ETH_ALEN) == 0)
+ if (ether_addr_equal(addr, mgmt->sa))
return 1;
}
@@ -2021,7 +2029,7 @@
}
os_memcpy(ie_buf, ie, ielen);
- if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) {
+ if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid, true) < 0) {
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -2149,7 +2157,8 @@
pmk, pmk_len,
sta->fils_erp_pmkid,
session_timeout,
- wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) {
+ wpa_auth_sta_key_mgmt(sta->wpa_sm),
+ NULL) < 0) {
wpa_printf(MSG_ERROR,
"FILS: Failed to add PMKSA cache entry based on ERP");
}
@@ -2520,8 +2529,8 @@
FILS_SESSION_LEN);
os_memcpy(fils->session, elems.fils_session, FILS_SESSION_LEN);
- fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
- WLAN_EID_EXT_WRAPPED_DATA);
+ fils_wd = ieee802_11_defrag(elems.wrapped_data, elems.wrapped_data_len,
+ true);
if (!fils_wd) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Missing wrapped data");
@@ -2612,7 +2621,7 @@
return -1;
}
- if (os_memcmp(entry->own_addr, own_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(entry->own_addr, own_addr)) {
wpa_printf(MSG_DEBUG,
"PASN: own addr " MACSTR " and PTKSA entry own addr "
MACSTR " differ",
@@ -2639,8 +2648,10 @@
struct pasn_data *pasn = sta->pasn;
struct ieee802_11_elems elems;
struct wpa_ie_data rsn_data;
+#ifdef CONFIG_FILS
struct wpa_pasn_params_data pasn_params;
struct wpabuf *wrapped_data = NULL;
+#endif /* CONFIG_FILS */
if (ieee802_11_parse_elems(mgmt->u.auth.variable,
len - offsetof(struct ieee80211_mgmt,
@@ -2690,8 +2701,8 @@
return;
}
if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
- wrapped_data = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
- WLAN_EID_EXT_WRAPPED_DATA);
+ wrapped_data = ieee802_11_defrag(elems.wrapped_data,
+ elems.wrapped_data_len, true);
if (!wrapped_data) {
wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
return;
@@ -2906,7 +2917,7 @@
goto fail;
}
- if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(mgmt->sa, hapd->own_addr)) {
wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
MAC2STR(sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -2914,8 +2925,8 @@
}
if (mld_sta &&
- (os_memcmp(sa, hapd->own_addr, ETH_ALEN) == 0 ||
- os_memcmp(sa, hapd->mld_addr, ETH_ALEN) == 0)) {
+ (ether_addr_equal(sa, hapd->own_addr) ||
+ ether_addr_equal(sa, hapd->mld_addr))) {
wpa_printf(MSG_INFO,
"Station " MACSTR " not allowed to authenticate",
MAC2STR(sa));
@@ -3068,12 +3079,13 @@
#ifdef CONFIG_IEEE80211BE
if (auth_transaction == 1) {
+ ap_sta_free_sta_profile(&sta->mld_info);
os_memset(&sta->mld_info, 0, sizeof(sta->mld_info));
if (mld_sta) {
u8 link_id = hapd->mld_link_id;
- sta->mld_info.mld_sta = true;
+ ap_sta_set_mld(sta, true);
sta->mld_assoc_link_id = link_id;
/*
@@ -3236,7 +3248,7 @@
* the MLD MAC address. It is the responsibility of the driver to
* handle the translations.
*/
- if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
dst = sta->addr;
bssid = hapd->mld_addr;
}
@@ -3281,7 +3293,7 @@
/* Do not assign an AID that is in use on any of the affiliated links
* when finding an AID for a non-AP MLD. */
- if (hapd->conf->mld_ap) {
+ if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
int j;
for (j = 0; j < MAX_NUM_MLD_LINKS; j++) {
@@ -3655,7 +3667,7 @@
wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len);
wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk,
- sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE);
+ sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE, NULL);
return WLAN_STATUS_SUCCESS;
}
@@ -3727,7 +3739,7 @@
goto end;
}
#ifdef CONFIG_IEEE80211BE
- if (sta->mld_info.mld_sta)
+ if (ap_sta_is_mld(hapd, sta))
wpa_auth_set_ml_info(sta->wpa_sm, hapd->mld_addr,
sta->mld_assoc_link_id, &sta->mld_info);
#endif /* CONFIG_IEEE80211BE */
@@ -4009,7 +4021,7 @@
}
#ifdef CONFIG_IEEE80211BE
- if (info->mld_sta) {
+ 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,
@@ -4309,22 +4321,23 @@
#ifdef CONFIG_IEEE80211BE
-static size_t ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
- u16 status_code,
- u8 *buf, size_t buflen)
+static void ieee80211_ml_build_assoc_resp(struct hostapd_data *hapd,
+ struct mld_link_info *link)
{
+ u8 buf[EHT_ML_MAX_STA_PROF_LEN];
u8 *p = buf;
+ size_t buflen = sizeof(buf);
/* Capability Info */
WPA_PUT_LE16(p, hostapd_own_capab_info(hapd));
p += 2;
/* Status Code */
- WPA_PUT_LE16(p, status_code);
+ WPA_PUT_LE16(p, link->status);
p += 2;
- if (status_code != WLAN_STATUS_SUCCESS)
- return p - buf;
+ if (link->status != WLAN_STATUS_SUCCESS)
+ goto out;
/* AID is not included */
p = hostapd_eid_supp_rates(hapd, p);
@@ -4362,20 +4375,24 @@
p += wpabuf_len(hapd->conf->assocresp_elements);
}
- return p - buf;
+out:
+ os_free(link->resp_sta_profile);
+ link->resp_sta_profile = os_memdup(buf, p - buf);
+ link->resp_sta_profile_len = link->resp_sta_profile ? p - buf : 0;
}
-static void ieee80211_ml_process_link(struct hostapd_data *hapd,
- struct sta_info *origin_sta,
- struct mld_link_info *link,
- const u8 *ies, size_t ies_len,
- bool reassoc)
+static int ieee80211_ml_process_link(struct hostapd_data *hapd,
+ struct sta_info *origin_sta,
+ struct mld_link_info *link,
+ const u8 *ies, size_t ies_len,
+ bool reassoc, bool offload)
{
struct ieee802_11_elems elems;
struct wpabuf *mlbuf = NULL;
struct sta_info *sta = NULL;
u16 status = WLAN_STATUS_SUCCESS;
+ int i;
wpa_printf(MSG_DEBUG, "MLD: link: link_id=%u, peer=" MACSTR,
hapd->mld_link_id, MAC2STR(link->peer_addr));
@@ -4401,7 +4418,7 @@
goto out;
}
- mlbuf = ieee802_11_defrag_mle(&elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ mlbuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len, true);
if (!mlbuf)
goto out;
@@ -4421,25 +4438,33 @@
goto out;
}
- sta->mld_info.mld_sta = true;
+ 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++) {
+ struct mld_link_info *li = &sta->mld_info.links[i];
- /*
- * Get the AID from the station on which the association was performed,
- * and mark it as used.
- */
- sta->aid = origin_sta->aid;
- if (sta->aid == 0) {
- wpa_printf(MSG_DEBUG, "MLD: link: No AID assigned");
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto out;
+ li->resp_sta_profile = NULL;
+ li->resp_sta_profile_len = 0;
}
- hapd->sta_aid[(sta->aid - 1) / 32] |= BIT((sta->aid - 1) % 32);
- sta->listen_interval = origin_sta->listen_interval;
- if (update_ht_state(hapd, sta) > 0)
- ieee802_11_update_beacons(hapd->iface);
+
+ if (!offload) {
+ /*
+ * Get the AID from the station on which the association was
+ * performed, and mark it as used.
+ */
+ sta->aid = origin_sta->aid;
+ if (sta->aid == 0) {
+ wpa_printf(MSG_DEBUG, "MLD: link: No AID assigned");
+ status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto out;
+ }
+ hapd->sta_aid[(sta->aid - 1) / 32] |= BIT((sta->aid - 1) % 32);
+ sta->listen_interval = origin_sta->listen_interval;
+ if (update_ht_state(hapd, sta) > 0)
+ ieee802_11_update_beacons(hapd->iface);
+ }
/* RSN Authenticator should always be the one on the original station */
wpa_auth_sta_deinit(sta->wpa_sm);
@@ -4465,20 +4490,23 @@
/* TODO: What other processing is required? */
- if (add_associated_sta(hapd, sta, reassoc))
+ if (!offload && add_associated_sta(hapd, sta, reassoc))
status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
out:
wpabuf_free(mlbuf);
link->status = status;
- wpa_printf(MSG_DEBUG, "MLD: link: status=%u", status);
- if (sta && status != WLAN_STATUS_SUCCESS)
- ap_free_sta(hapd, sta);
+ if (!offload)
+ ieee80211_ml_build_assoc_resp(hapd, link);
- link->resp_sta_profile_len =
- ieee80211_ml_build_assoc_resp(hapd, link->status,
- link->resp_sta_profile,
- sizeof(link->resp_sta_profile));
+ wpa_printf(MSG_DEBUG, "MLD: link: status=%u", status);
+ if (status != WLAN_STATUS_SUCCESS) {
+ if (sta)
+ ap_free_sta(hapd, sta);
+ return -1;
+ }
+
+ return 0;
}
@@ -4497,16 +4525,17 @@
#endif /* CONFIG_IEEE80211BE */
-static void hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
- struct sta_info *sta,
- const u8 *ies, size_t ies_len,
- bool reassoc)
+int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *ies, size_t ies_len,
+ bool reassoc, int tx_link_status,
+ bool offload)
{
#ifdef CONFIG_IEEE80211BE
unsigned int i, j;
if (!hostapd_is_mld_ap(hapd))
- return;
+ return 0;
/*
* This is not really needed, but make the interaction with the RSN
@@ -4536,22 +4565,29 @@
break;
}
- if (!iface || j == hapd->iface->interfaces->count) {
+ if (!iface || j == hapd->iface->interfaces->count ||
+ TEST_FAIL()) {
wpa_printf(MSG_DEBUG,
"MLD: No link match for link_id=%u", i);
link->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
- link->resp_sta_profile_len =
- ieee80211_ml_build_assoc_resp(
- hapd, link->status,
- link->resp_sta_profile,
- sizeof(link->resp_sta_profile));
+ if (!offload)
+ ieee80211_ml_build_assoc_resp(hapd, link);
+ } else if (tx_link_status != WLAN_STATUS_SUCCESS) {
+ /* TX link rejected the connection */
+ link->status = WLAN_STATUS_DENIED_TX_LINK_NOT_ACCEPTED;
+ if (!offload)
+ ieee80211_ml_build_assoc_resp(hapd, link);
} else {
- ieee80211_ml_process_link(iface->bss[0], sta, link,
- ies, ies_len, reassoc);
+ if (ieee80211_ml_process_link(iface->bss[0], sta, link,
+ ies, ies_len, reassoc,
+ offload))
+ return -1;
}
}
#endif /* CONFIG_IEEE80211BE */
+
+ return 0;
}
@@ -4589,7 +4625,7 @@
bool mld_link_sta = false;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
u8 mld_link_id = hapd->mld_link_id;
mld_link_sta = sta->mld_assoc_link_id != mld_link_id;
@@ -4700,7 +4736,7 @@
static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 status_code, int reassoc,
const u8 *ies, size_t ies_len, int rssi,
- int omit_rsnxe)
+ int omit_rsnxe, bool allow_mld_addr_trans)
{
int send_len;
u8 *buf;
@@ -4750,7 +4786,7 @@
* Once a non-AP MLD is added to the driver, the addressing should use
* MLD MAC address.
*/
- if (hapd->conf->mld_ap && sta && sta->mld_info.mld_sta)
+ if (ap_sta_is_mld(hapd, sta) && allow_mld_addr_trans)
sa = hapd->mld_addr;
#endif /* CONFIG_IEEE80211BE */
@@ -4893,7 +4929,7 @@
#ifdef CONFIG_IEEE80211BE
if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
if (hapd->conf->mld_ap)
- p = hostapd_eid_eht_basic_ml(hapd, p, sta, false);
+ p = hostapd_eid_eht_ml_assoc(hapd, sta, p);
p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
p = hostapd_eid_eht_operation(hapd, p);
}
@@ -5116,7 +5152,8 @@
reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS,
sta->fils_pending_assoc_is_reassoc,
sta->fils_pending_assoc_req,
- sta->fils_pending_assoc_req_len, 0, 0);
+ sta->fils_pending_assoc_req_len, 0, 0,
+ true);
os_free(sta->fils_pending_assoc_req);
sta->fils_pending_assoc_req = NULL;
sta->fils_pending_assoc_req_len = 0;
@@ -5151,6 +5188,48 @@
#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211BE
+static struct sta_info * handle_mlo_translate(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, bool reassoc,
+ struct hostapd_data **assoc_hapd)
+{
+ struct sta_info *sta;
+ struct ieee802_11_elems elems;
+ u8 mld_addr[ETH_ALEN];
+ const u8 *pos;
+
+ if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be)
+ return NULL;
+
+ if (reassoc) {
+ len -= IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req);
+ pos = mgmt->u.reassoc_req.variable;
+ } else {
+ len -= IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req);
+ pos = mgmt->u.assoc_req.variable;
+ }
+
+ if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed)
+ return NULL;
+
+ if (hostapd_process_ml_assoc_req_addr(hapd, elems.basic_mle,
+ elems.basic_mle_len,
+ mld_addr))
+ return NULL;
+
+ sta = ap_get_sta(hapd, mld_addr);
+ if (!sta)
+ return NULL;
+
+ wpa_printf(MSG_DEBUG, "MLD: assoc: mld=" MACSTR ", link=" MACSTR,
+ MAC2STR(mld_addr), MAC2STR(mgmt->sa));
+
+ return hostapd_ml_get_assoc_sta(hapd, sta, assoc_hapd);
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
static void handle_assoc(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int reassoc, int rssi)
@@ -5167,6 +5246,7 @@
#endif /* CONFIG_FILS */
int omit_rsnxe = 0;
bool set_beacon = false;
+ bool mld_addrs_not_translated = false;
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
sizeof(mgmt->u.assoc_req))) {
@@ -5224,6 +5304,28 @@
}
sta = ap_get_sta(hapd, mgmt->sa);
+
+#ifdef CONFIG_IEEE80211BE
+ /*
+ * It is possible that the association frame is from an associated
+ * non-AP MLD station, that tries to re-associate using different link
+ * addresses. In such a case, try to find the station based on the AP
+ * MLD MAC address.
+ */
+ if (!sta) {
+ struct hostapd_data *assoc_hapd;
+
+ sta = handle_mlo_translate(hapd, mgmt, len, reassoc,
+ &assoc_hapd);
+ if (sta) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Switching to assoc hapd/station");
+ hapd = assoc_hapd;
+ mld_addrs_not_translated = true;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_IEEE80211R_AP
if (sta && sta->auth_alg == WLAN_AUTH_FT &&
(sta->flags & WLAN_STA_AUTH) == 0) {
@@ -5500,8 +5602,9 @@
* issues with processing other non-Data Class 3 frames during this
* window.
*/
- if (resp == WLAN_STATUS_SUCCESS)
- hostapd_process_assoc_ml_info(hapd, sta, pos, left, reassoc);
+ if (sta)
+ hostapd_process_assoc_ml_info(hapd, sta, pos, left, reassoc,
+ resp, false);
if (resp == WLAN_STATUS_SUCCESS && sta &&
add_associated_sta(hapd, sta, reassoc))
@@ -5545,8 +5648,12 @@
#endif /* CONFIG_FILS */
if (resp >= 0)
- reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc,
- pos, left, rssi, omit_rsnxe);
+ reply_res = send_assoc_resp(hapd,
+ mld_addrs_not_translated ?
+ NULL : sta,
+ mgmt->sa, resp, reassoc,
+ pos, left, rssi, omit_rsnxe,
+ !mld_addrs_not_translated);
os_free(tmp);
/*
@@ -5637,44 +5744,6 @@
}
-#ifdef CONFIG_IEEE80211BE
-static struct sta_info *
-hostapd_ml_get_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
- struct hostapd_data **assoc_hapd)
-{
- struct hostapd_data *other_hapd = NULL;
- struct sta_info *tmp_sta;
-
- *assoc_hapd = hapd;
-
- /* The station is the one on which the association was performed */
- if (sta->mld_assoc_link_id == hapd->mld_link_id)
- return sta;
-
- other_hapd = hostapd_mld_get_link_bss(hapd, sta->mld_assoc_link_id);
- if (!other_hapd) {
- wpa_printf(MSG_DEBUG, "MLD: No link match for link_id=%u",
- sta->mld_assoc_link_id);
- return sta;
- }
-
- /*
- * Iterate over the stations and find the one with the matching link ID
- * and association ID.
- */
- for (tmp_sta = other_hapd->sta_list; tmp_sta; tmp_sta = tmp_sta->next) {
- if (tmp_sta->mld_assoc_link_id == sta->mld_assoc_link_id &&
- tmp_sta->aid == sta->aid) {
- *assoc_hapd = other_hapd;
- return tmp_sta;
- }
- }
-
- return sta;
-}
-#endif /* CONFIG_IEEE80211BE */
-
-
static bool hostapd_ml_handle_disconnect(struct hostapd_data *hapd,
struct sta_info *sta,
const struct ieee80211_mgmt *mgmt,
@@ -5693,6 +5762,8 @@
* the information about all the other links.
*/
assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
+ 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++) {
@@ -5964,6 +6035,25 @@
return 1;
}
#endif /* CONFIG_DPP */
+#ifdef CONFIG_NAN_USD
+ if (mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
+ len >= IEEE80211_HDRLEN + 5 &&
+ mgmt->u.action.u.vs_public_action.action ==
+ WLAN_PA_VENDOR_SPECIFIC &&
+ WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
+ OUI_WFA &&
+ mgmt->u.action.u.vs_public_action.variable[0] ==
+ NAN_OUI_TYPE) {
+ const u8 *pos, *end;
+
+ pos = mgmt->u.action.u.vs_public_action.variable;
+ end = ((const u8 *) mgmt) + len;
+ pos++;
+ hostapd_nan_usd_rx_sdf(hapd, mgmt->sa, freq,
+ pos, end - pos);
+ return 1;
+ }
+#endif /* CONFIG_NAN_USD */
if (hapd->public_action_cb) {
hapd->public_action_cb(hapd->public_action_cb_ctx,
(u8 *) mgmt, len, freq);
@@ -5982,9 +6072,11 @@
return 1;
}
break;
+#ifndef CONFIG_NO_RRM
case WLAN_ACTION_RADIO_MEASUREMENT:
hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
return 1;
+#endif /* CONFIG_NO_RRM */
}
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -6069,6 +6161,10 @@
int ret = 0;
unsigned int freq;
int ssi_signal = fi ? fi->ssi_signal : 0;
+#ifdef CONFIG_NAN_USD
+ static const u8 nan_network_id[ETH_ALEN] =
+ { 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 };
+#endif /* CONFIG_NAN_USD */
if (len < 24)
return 0;
@@ -6084,7 +6180,7 @@
if (is_multicast_ether_addr(mgmt->sa) ||
is_zero_ether_addr(mgmt->sa) ||
- os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
+ ether_addr_equal(mgmt->sa, hapd->own_addr)) {
/* Do not process any frames with unexpected/invalid SA so that
* we do not add any state for unexpected STA addresses or end
* up sending out frames to unexpected destination. */
@@ -6110,9 +6206,9 @@
#endif /* CONFIG_MESH */
#ifdef CONFIG_IEEE80211BE
!(hapd->conf->mld_ap &&
- os_memcmp(hapd->mld_addr, mgmt->bssid, ETH_ALEN) == 0) &&
+ ether_addr_equal(hapd->mld_addr, mgmt->bssid)) &&
#endif /* CONFIG_IEEE80211BE */
- os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(mgmt->bssid, hapd->own_addr)) {
wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address",
MAC2STR(mgmt->bssid));
return 0;
@@ -6133,9 +6229,12 @@
stype != WLAN_FC_STYPE_ACTION) &&
#ifdef CONFIG_IEEE80211BE
!(hapd->conf->mld_ap &&
- os_memcmp(hapd->mld_addr, mgmt->bssid, ETH_ALEN) == 0) &&
+ ether_addr_equal(hapd->mld_addr, mgmt->bssid)) &&
#endif /* CONFIG_IEEE80211BE */
- os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
+#ifdef CONFIG_NAN_USD
+ !ether_addr_equal(mgmt->da, nan_network_id) &&
+#endif /* CONFIG_NAN_USD */
+ !ether_addr_equal(mgmt->da, hapd->own_addr)) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"MGMT: DA=" MACSTR " not our address",
@@ -6285,6 +6384,8 @@
struct mld_link_info *link,
bool ok)
{
+ bool updated = false;
+
if (!ok) {
hostapd_logger(hapd, link->peer_addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -6305,9 +6406,11 @@
sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
if (!hapd->conf->ieee802_1x && !hapd->conf->wpa)
- ap_sta_set_authorized(hapd, sta, 1);
+ updated = ap_sta_set_authorized_flag(hapd, sta, 1);
hostapd_set_sta_flags(hapd, sta);
+ if (updated)
+ ap_sta_set_authorized_event(hapd, sta, 1);
/*
* TODOs:
@@ -6340,7 +6443,7 @@
struct hostapd_data *tmp_hapd =
hapd->iface->interfaces->iface[i]->bss[0];
- if (tmp_hapd->conf->mld_ap ||
+ if (!tmp_hapd->conf->mld_ap ||
hapd->conf->mld_id != tmp_hapd->conf->mld_id)
continue;
@@ -6379,7 +6482,7 @@
}
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta &&
+ if (ap_sta_is_mld(hapd, sta) &&
hapd->mld_link_id != sta->mld_assoc_link_id) {
/* See ieee80211_ml_link_sta_assoc_cb() for the MLD case */
wpa_printf(MSG_DEBUG,
@@ -6589,7 +6692,9 @@
size_t len, int ok)
{
struct sta_info *sta;
+#ifndef CONFIG_NO_RRM
const struct rrm_measurement_report_element *report;
+#endif /* CONFIG_NO_RRM */
#ifdef CONFIG_DPP
if (len >= IEEE80211_HDRLEN + 6 &&
@@ -6643,6 +6748,7 @@
}
#endif /* CONFIG_HS20 */
+#ifndef CONFIG_NO_RRM
if (len < 24 + 5 + sizeof(*report))
return;
report = (const struct rrm_measurement_report_element *)
@@ -6653,6 +6759,7 @@
report->len >= 3 &&
report->type == MEASURE_TYPE_BEACON)
hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok);
+#endif /* CONFIG_NO_RRM */
}
@@ -6864,7 +6971,7 @@
wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
MACSTR, MAC2STR(src));
if (is_multicast_ether_addr(src) || is_zero_ether_addr(src) ||
- os_memcmp(src, hapd->own_addr, ETH_ALEN) == 0) {
+ ether_addr_equal(src, hapd->own_addr)) {
/* Broadcast bit set in SA or unexpected SA?! Ignore the frame
* silently. */
return;
@@ -6963,21 +7070,35 @@
tx_pwr_intrpn = REGULATORY_CLIENT_EIRP_PSD;
/* Default Transmit Power Envelope for Global Operating Class */
- tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
+ if (hapd->iconf->reg_def_cli_eirp_psd != -1)
+ tx_pwr = hapd->iconf->reg_def_cli_eirp_psd;
+ else
+ tx_pwr = REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT * 2;
+
eid = hostapd_add_tpe_info(eid, tx_pwr_count, tx_pwr_intrpn,
REG_DEFAULT_CLIENT, tx_pwr);
/* Indoor Access Point must include an additional TPE for
* subordinate devices */
- if (iconf->he_6ghz_reg_pwr_type == HE_6GHZ_INDOOR_AP) {
+ if (he_reg_is_indoor(iconf->he_6ghz_reg_pwr_type)) {
/* TODO: Extract PSD limits from channel data */
- tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
+ if (hapd->iconf->reg_sub_cli_eirp_psd != -1)
+ tx_pwr = hapd->iconf->reg_sub_cli_eirp_psd;
+ else
+ tx_pwr = REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT * 2;
eid = hostapd_add_tpe_info(eid, tx_pwr_count,
tx_pwr_intrpn,
REG_SUBORDINATE_CLIENT,
tx_pwr);
}
+ if (iconf->reg_def_cli_eirp != -1 &&
+ he_reg_is_sp(iconf->he_6ghz_reg_pwr_type))
+ eid = hostapd_add_tpe_info(
+ eid, tx_pwr_count, REGULATORY_CLIENT_EIRP,
+ REG_DEFAULT_CLIENT,
+ hapd->iconf->reg_def_cli_eirp);
+
return eid;
}
#endif /* CONFIG_IEEE80211AX */
@@ -7392,7 +7513,7 @@
/* BSS parameters */
*eid++ = nr->bss_parameters;
/* 20 MHz PSD */
- *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1;
+ *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER;
len += RNR_TBTT_INFO_LEN;
*size_offset = (eid - size_offset) - 1;
}
@@ -7402,17 +7523,98 @@
}
+static bool hostapd_eid_rnr_bss(struct hostapd_data *hapd,
+ struct hostapd_data *reporting_hapd,
+ struct mbssid_ie_profiles *skip_profiles,
+ size_t i, u8 *tbtt_count, size_t *len,
+ u8 **pos)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_data *bss = iface->bss[i];
+ u8 bss_param = 0;
+ bool ap_mld = false;
+ u8 *eid = *pos;
+
+#ifdef CONFIG_IEEE80211BE
+ ap_mld = !!hapd->conf->mld_ap;
+#endif /* CONFIG_IEEE80211BE */
+
+ if (!bss || !bss->conf || !bss->started ||
+ bss == reporting_hapd || bss->conf->ignore_broadcast_ssid)
+ return false;
+
+ if (skip_profiles
+ && i >= skip_profiles->start && i < skip_profiles->end)
+ return false;
+
+ if (*len + RNR_TBTT_INFO_LEN > 255 ||
+ *tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+ return true;
+
+ *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
+ os_memcpy(eid, bss->own_addr, ETH_ALEN);
+ eid += ETH_ALEN;
+ os_memcpy(eid, &bss->conf->ssid.short_ssid, 4);
+ eid += 4;
+ if (bss->conf->ssid.short_ssid == reporting_hapd->conf->ssid.short_ssid)
+ bss_param |= RNR_BSS_PARAM_SAME_SSID;
+
+ if (iface->conf->mbssid != MBSSID_DISABLED && iface->num_bss > 1) {
+ bss_param |= RNR_BSS_PARAM_MULTIPLE_BSSID;
+ if (bss == hostapd_mbssid_get_tx_bss(hapd))
+ bss_param |= RNR_BSS_PARAM_TRANSMITTED_BSSID;
+ }
+
+ if (is_6ghz_op_class(hapd->iconf->op_class) &&
+ bss->conf->unsol_bcast_probe_resp_interval)
+ bss_param |= RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE;
+
+ bss_param |= RNR_BSS_PARAM_CO_LOCATED;
+
+ *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 (reporting_hapd->conf->mld_ap &&
+ bss->conf->mld_id == reporting_hapd->conf->mld_id)
+ *eid++ = 0;
+ else
+ *eid++ = hapd->conf->mld_id;
+
+ *eid++ = hapd->mld_link_id | ((param_ch & 0xF) << 4);
+ *eid = (param_ch >> 4) & 0xF;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->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 */
+ }
+
+ (*tbtt_count)++;
+ *pos = eid;
+
+ return false;
+}
+
+
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 hostapd_data *bss;
struct hostapd_iface *iface = hapd->iface;
size_t i, start = 0;
size_t len = *current_len;
u8 *tbtt_count_pos, *eid_start = eid, *size_offset = (eid - len) + 1;
- u8 tbtt_count = 0, op_class, channel, bss_param;
+ u8 tbtt_count = 0, op_class, channel;
bool ap_mld = false;
#ifdef CONFIG_IEEE80211BE
@@ -7447,62 +7649,10 @@
len += RNR_TBTT_HEADER_LEN;
for (i = start; i < iface->num_bss; i++) {
- bss_param = 0;
- bss = iface->bss[i];
- if (!bss || !bss->conf || !bss->started)
- continue;
-
- if (bss == reporting_hapd ||
- bss->conf->ignore_broadcast_ssid)
- continue;
-
- if (skip_profiles &&
- i >= skip_profiles->start && i < skip_profiles->end)
- continue;
-
- if (len + RNR_TBTT_INFO_LEN > 255 ||
- tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+ if (hostapd_eid_rnr_bss(hapd, reporting_hapd,
+ skip_profiles, i,
+ &tbtt_count, &len, &eid))
break;
-
- *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
- os_memcpy(eid, bss->own_addr, ETH_ALEN);
- eid += ETH_ALEN;
- os_memcpy(eid, &bss->conf->ssid.short_ssid, 4);
- eid += 4;
- if (bss->conf->ssid.short_ssid ==
- reporting_hapd->conf->ssid.short_ssid)
- bss_param |= RNR_BSS_PARAM_SAME_SSID;
-
- if (iface->conf->mbssid != MBSSID_DISABLED &&
- iface->num_bss > 1) {
- bss_param |= RNR_BSS_PARAM_MULTIPLE_BSSID;
- if (bss == hostapd_mbssid_get_tx_bss(hapd))
- bss_param |=
- RNR_BSS_PARAM_TRANSMITTED_BSSID;
- }
-
- if (is_6ghz_op_class(hapd->iconf->op_class) &&
- bss->conf->unsol_bcast_probe_resp_interval)
- bss_param |=
- RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE;
-
- bss_param |= RNR_BSS_PARAM_CO_LOCATED;
-
- *eid++ = bss_param;
- *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1;
-
- if (!ap_mld) {
- len += RNR_TBTT_INFO_LEN;
- } else {
-#ifdef CONFIG_IEEE80211BE
- *eid++ = hapd->conf->mld_id;
- *eid++ = hapd->mld_link_id | (1 << 4);
- *eid++ = 0;
- len += RNR_TBTT_INFO_MLD_LEN;
-#endif /* CONFIG_IEEE80211BE */
- }
-
- tbtt_count += 1;
}
start = i;
@@ -7579,7 +7729,7 @@
case WLAN_FC_STYPE_ACTION:
if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
- eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
+ eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
¤t_len, NULL);
break;
@@ -7610,7 +7760,18 @@
size_t known_bss_len)
{
struct hostapd_data *tx_bss = hostapd_mbssid_get_tx_bss(hapd);
- size_t len = 3, i;
+ size_t len, i;
+
+ /* Element ID: 1 octet
+ * Length: 1 octet
+ * MaxBSSID Indicator: 1 octet
+ * Optional Subelements: vatiable
+ *
+ * Total fixed length: 3 octets
+ *
+ * 1 octet in len for the MaxBSSID Indicator field.
+ */
+ len = 1;
for (i = *bss_index; i < hapd->iface->num_bss; i++) {
struct hostapd_data *bss = hapd->iface->bss[i];
@@ -7663,7 +7824,9 @@
}
*bss_index = i;
- return len;
+
+ /* Add 2 octets to get the full size of the element */
+ return len + 2;
}
@@ -7792,13 +7955,14 @@
eid += 2 + rsnx[1];
}
}
+ /* List of Element ID values in increasing order */
if (!rsn && hostapd_wpa_ie(tx_bss, WLAN_EID_RSN))
non_inherit_ie[ie_count++] = WLAN_EID_RSN;
- if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
- non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
if (hapd->conf->xrates_supported &&
!bss->conf->xrates_supported)
non_inherit_ie[ie_count++] = WLAN_EID_EXT_SUPP_RATES;
+ if (!rsnx && hostapd_wpa_ie(tx_bss, WLAN_EID_RSNX))
+ non_inherit_ie[ie_count++] = WLAN_EID_RSNX;
if (ie_count) {
*eid++ = WLAN_EID_EXTENSION;
*eid++ = 2 + ie_count + 1;
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 4b58fee..5fd380a 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -23,6 +23,7 @@
struct sae_pk;
struct sae_pt;
struct sae_password_entry;
+struct mld_info;
int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
struct hostapd_frame_info *fi);
@@ -88,8 +89,14 @@
const struct ieee80211_eht_capabilities *src,
struct ieee80211_eht_capabilities *dest,
size_t len);
-u8 * hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid,
- struct sta_info *info, bool include_mld_id);
+u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd,
+ struct mld_info *mld_info,
+ u8 *eid, bool include_mld_id);
+u8 * hostapd_eid_eht_ml_assoc(struct hostapd_data *hapd, struct sta_info *info,
+ u8 *eid);
+size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd,
+ struct mld_info *info,
+ bool include_mld_id);
struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd);
const u8 * hostapd_process_ml_auth(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
@@ -97,6 +104,9 @@
u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
struct ieee802_11_elems *elems,
struct sta_info *sta);
+int hostapd_process_ml_assoc_req_addr(struct hostapd_data *hapd,
+ const u8 *basic_mle, size_t basic_mle_len,
+ u8 *mld_addr);
int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ht_capab);
@@ -245,5 +255,13 @@
struct sta_info *sta, const char *rx_id,
struct sae_password_entry **pw_entry,
struct sae_pt **s_pt, const struct sae_pk **s_pk);
+struct sta_info * hostapd_ml_get_assoc_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct hostapd_data **assoc_hapd);
+int hostapd_process_assoc_ml_info(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *ies, size_t ies_len,
+ bool reassoc, int tx_link_status,
+ bool offload);
#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 4277d82..e723ae7 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -84,7 +84,7 @@
os_get_reltime(&now);
for (entry = hapd->acl_cache; entry; entry = entry->next) {
- if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0)
+ if (!ether_addr_equal(entry->addr, addr))
continue;
if (os_reltime_expired(&now, &entry->timestamp,
@@ -281,7 +281,7 @@
query = hapd->acl_queries;
while (query) {
- if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(query->addr, addr)) {
/* pending query in RADIUS retransmit queue;
* do not generate a new one */
return HOSTAPD_ACL_PENDING;
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
index c1ccda4..840fc20 100644
--- a/src/ap/ieee802_11_eht.c
+++ b/src/ap/ieee802_11_eht.c
@@ -44,12 +44,13 @@
static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass,
- u8 he_oper_chwidth, const u8 *he_phy_cap,
+ int he_oper_chwidth, const u8 *he_phy_cap,
const u8 *eht_phy_cap)
{
u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
bool band24, band5, band6;
u8 he_phy_cap_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
+ u8 cap_chwidth;
switch (he_oper_chwidth) {
case CONF_OPER_CHWIDTH_80P80MHZ:
@@ -66,7 +67,11 @@
break;
}
- he_phy_cap_chwidth &= he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
+ cap_chwidth = he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
+ if (he_oper_chwidth != -1)
+ he_phy_cap_chwidth &= cap_chwidth;
+ else
+ he_phy_cap_chwidth = cap_chwidth;
band24 = mode == HOSTAPD_MODE_IEEE80211B ||
mode == HOSTAPD_MODE_IEEE80211G ||
@@ -199,12 +204,33 @@
struct ieee80211_eht_operation *oper;
u8 *pos = eid, seg0 = 0, seg1 = 0;
enum oper_chan_width chwidth;
- size_t elen = 1 + 4 + 3;
+ size_t elen = 1 + 4;
+ bool eht_oper_info_present;
+ u16 punct_bitmap = conf->punct_bitmap;
if (!hapd->iface->current_mode)
return eid;
- if (hapd->iconf->punct_bitmap)
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!punct_bitmap && hapd->conf->eht_oper_puncturing_override) {
+ wpa_printf(MSG_DEBUG, "EHT: Puncturing mask override=0x%x",
+ hapd->conf->eht_oper_puncturing_override);
+ punct_bitmap = hapd->conf->eht_oper_puncturing_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (is_6ghz_op_class(conf->op_class))
+ chwidth = op_class_to_ch_width(conf->op_class);
+ else
+ chwidth = conf->eht_oper_chwidth;
+
+ eht_oper_info_present = chwidth == CONF_OPER_CHWIDTH_320MHZ ||
+ punct_bitmap;
+
+ if (eht_oper_info_present)
+ elen += 3;
+
+ if (punct_bitmap)
elen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
*pos++ = WLAN_EID_EXTENSION;
@@ -212,7 +238,10 @@
*pos++ = WLAN_EID_EXT_EHT_OPERATION;
oper = (struct ieee80211_eht_operation *) pos;
- oper->oper_params = EHT_OPER_INFO_PRESENT;
+ oper->oper_params = 0;
+
+ if (hapd->iconf->eht_default_pe_duration)
+ oper->oper_params |= EHT_OPER_DEFAULT_PE_DURATION;
/* TODO: Fill in appropriate EHT-MCS max Nss information */
oper->basic_eht_mcs_nss_set[0] = 0x11;
@@ -220,11 +249,10 @@
oper->basic_eht_mcs_nss_set[2] = 0x00;
oper->basic_eht_mcs_nss_set[3] = 0x00;
- if (is_6ghz_op_class(conf->op_class))
- chwidth = op_class_to_ch_width(conf->op_class);
- else
- chwidth = conf->eht_oper_chwidth;
+ if (!eht_oper_info_present)
+ return pos + elen;
+ oper->oper_params |= EHT_OPER_INFO_PRESENT;
seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
switch (chwidth) {
@@ -258,10 +286,10 @@
oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
oper->oper_info.ccfs1 = seg1;
- if (hapd->iconf->punct_bitmap) {
+ if (punct_bitmap) {
oper->oper_params |= EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT;
oper->oper_info.disabled_chan_bitmap =
- host_to_le16(hapd->iconf->punct_bitmap);
+ host_to_le16(punct_bitmap);
}
return pos + elen;
@@ -338,9 +366,8 @@
static bool ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,
- u8 opclass, u8 he_oper_chwidth,
- const u8 *he_cap, const u8 *eht_cap,
- size_t len)
+ u8 opclass, const u8 *he_cap,
+ const u8 *eht_cap, size_t len)
{
const struct ieee80211_he_capabilities *he_capab;
struct ieee80211_eht_capabilities *cap;
@@ -355,8 +382,8 @@
if (len < cap_len)
return true;
- cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_oper_chwidth,
- he_phy_cap, cap->phy_cap);
+ cap_len += ieee80211_eht_mcs_set_size(mode, opclass, -1, he_phy_cap,
+ cap->phy_cap);
if (len < cap_len)
return true;
@@ -380,7 +407,6 @@
!he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
!eht_capab ||
ieee80211_invalid_eht_cap_size(mode, hapd->iconf->op_class,
- hapd->iconf->he_oper_chwidth,
he_capab, eht_capab,
eht_capab_len) ||
!check_valid_eht_mcs(hapd, eht_capab, opmode)) {
@@ -421,8 +447,9 @@
}
-u8 * hostapd_eid_eht_basic_ml(struct hostapd_data *hapd, u8 *eid,
- struct sta_info *info, bool include_mld_id)
+static u8 * hostapd_eid_eht_basic_ml_common(struct hostapd_data *hapd,
+ u8 *eid, struct mld_info *mld_info,
+ bool include_mld_id)
{
struct wpabuf *buf;
u16 control;
@@ -454,7 +481,8 @@
* BSS Parameters Change Count (1) + EML Capabilities (2) +
* MLD Capabilities and Operations (2)
*/
- common_info_len = 13;
+#define EHT_ML_COMMON_INFO_LEN 13
+ common_info_len = EHT_ML_COMMON_INFO_LEN;
if (include_mld_id) {
/* AP MLD ID */
@@ -489,12 +517,12 @@
wpabuf_put_u8(buf, hapd->conf->mld_id);
}
- if (!info)
+ if (!mld_info)
goto out;
/* Add link info for the other links */
for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- struct mld_link_info *link = &info->mld_info.links[link_id];
+ struct mld_link_info *link = &mld_info->links[link_id];
struct hostapd_data *link_bss;
/*
@@ -502,8 +530,9 @@
* beacon interval (2) + TSF offset (8) + DTIM info (2) + BSS
* parameters change counter (1) + station profile length.
*/
- const size_t fixed_len = 22;
- size_t total_len = fixed_len + link->resp_sta_profile_len;
+#define EHT_ML_STA_INFO_LEN 22
+ size_t total_len = EHT_ML_STA_INFO_LEN +
+ link->resp_sta_profile_len;
/* Skip the local one */
if (link_id == hapd->mld_link_id || !link->valid)
@@ -537,7 +566,7 @@
/* STA Info */
/* STA Info Length */
- wpabuf_put_u8(buf, fixed_len - 2);
+ wpabuf_put_u8(buf, EHT_ML_STA_INFO_LEN - 2);
wpabuf_put_data(buf, link->local_addr, ETH_ALEN);
wpabuf_put_le16(buf, link_bss->iconf->beacon_int);
@@ -552,9 +581,10 @@
wpabuf_put_le16(buf, link_bss->conf->dtim_period);
/* BSS Parameters Change Count */
- /* TODO: Currently hard code the BSS Parameters Change Count to
- * 0x1 */
- wpabuf_put_u8(buf, 0x1);
+ wpabuf_put_u8(buf, hapd->eht_mld_bss_param_change);
+
+ if (!link->resp_sta_profile)
+ continue;
/* Fragment the sub element if needed */
if (total_len <= 255) {
@@ -564,7 +594,7 @@
ptr = link->resp_sta_profile;
len = link->resp_sta_profile_len;
- slice_len = 255 - fixed_len;
+ slice_len = 255 - EHT_ML_STA_INFO_LEN;
wpabuf_put_data(buf, ptr, slice_len);
len -= slice_len;
@@ -625,6 +655,151 @@
}
+static u8 * hostapd_eid_eht_reconf_ml(struct hostapd_data *hapd, u8 *eid)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ struct hostapd_data *other_hapd;
+ u16 control;
+ u8 *pos = eid;
+ unsigned int i;
+
+ wpa_printf(MSG_DEBUG, "MLD: Reconfiguration ML");
+
+ /* First check if the element needs to be added */
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
+
+ wpa_printf(MSG_DEBUG, "MLD: Reconfiguration ML: %u",
+ other_hapd->eht_mld_link_removal_count);
+
+ if (other_hapd->eht_mld_link_removal_count)
+ break;
+ }
+
+ /* No link is going to be removed */
+ if (i == hapd->iface->interfaces->count)
+ return eid;
+
+ wpa_printf(MSG_DEBUG, "MLD: Reconfiguration ML: Adding element");
+
+ /* The length will be set at the end */
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 0;
+ *pos++ = WLAN_EID_EXT_MULTI_LINK;
+
+ /* Set the Multi-Link Control field */
+ control = MULTI_LINK_CONTROL_TYPE_RECONF;
+ WPA_PUT_LE16(pos, control);
+ pos += 2;
+
+ /* Common Info doesn't include any information */
+ *pos++ = 1;
+
+ /* Add the per station profiles */
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ other_hapd = hapd->iface->interfaces->iface[i]->bss[0];
+ if (!other_hapd->eht_mld_link_removal_count)
+ continue;
+
+ /* Subelement ID is 0 */
+ *pos++ = 0;
+ *pos++ = 5;
+
+ control = other_hapd->mld_link_id |
+ EHT_PER_STA_RECONF_CTRL_AP_REMOVAL_TIMER;
+
+ WPA_PUT_LE16(pos, control);
+ pos += 2;
+
+ /* STA profile length */
+ *pos++ = 3;
+
+ WPA_PUT_LE16(pos, other_hapd->eht_mld_link_removal_count);
+ pos += 2;
+ }
+
+ eid[1] = pos - eid - 2;
+
+ wpa_hexdump(MSG_DEBUG, "MLD: Reconfiguration ML", eid, eid[1] + 2);
+ return pos;
+#else /* CONFIG_TESTING_OPTIONS */
+ return eid;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
+static size_t hostapd_eid_eht_ml_len(struct mld_info *info,
+ bool include_mld_id)
+{
+ size_t len = 0;
+ size_t eht_ml_len = 2 + EHT_ML_COMMON_INFO_LEN;
+ u8 link_id;
+
+ if (include_mld_id)
+ eht_ml_len++;
+
+ for (link_id = 0; info && link_id < ARRAY_SIZE(info->links);
+ link_id++) {
+ struct mld_link_info *link;
+ size_t sta_len = EHT_ML_STA_INFO_LEN;
+
+ link = &info->links[link_id];
+ if (!link->valid)
+ continue;
+
+ sta_len += link->resp_sta_profile_len;
+
+ /* Element data and (fragmentation) headers */
+ eht_ml_len += sta_len;
+ eht_ml_len += 2 + sta_len / 255 * 2;
+ }
+
+ /* Element data */
+ len += eht_ml_len;
+
+ /* First header (254 bytes of data) */
+ len += 3;
+
+ /* Fragmentation headers; +1 for shorter first chunk */
+ len += (eht_ml_len + 1) / 255 * 2;
+
+ return len;
+}
+#undef EHT_ML_COMMON_INFO_LEN
+#undef EHT_ML_STA_INFO_LEN
+
+
+u8 * hostapd_eid_eht_ml_beacon(struct hostapd_data *hapd,
+ struct mld_info *info,
+ u8 *eid, bool include_mld_id)
+{
+ eid = hostapd_eid_eht_basic_ml_common(hapd, eid, info, include_mld_id);
+ return hostapd_eid_eht_reconf_ml(hapd, eid);
+}
+
+
+
+u8 * hostapd_eid_eht_ml_assoc(struct hostapd_data *hapd, struct sta_info *info,
+ u8 *eid)
+{
+ if (!ap_sta_is_mld(hapd, info))
+ return eid;
+
+ eid = hostapd_eid_eht_basic_ml_common(hapd, eid, &info->mld_info,
+ false);
+ ap_sta_free_sta_profile(&info->mld_info);
+ return hostapd_eid_eht_reconf_ml(hapd, eid);
+}
+
+
+size_t hostapd_eid_eht_ml_beacon_len(struct hostapd_data *hapd,
+ struct mld_info *info,
+ bool include_mld_id)
+{
+ return hostapd_eid_eht_ml_len(info, include_mld_id);
+}
+
+
struct wpabuf * hostapd_ml_auth_resp(struct hostapd_data *hapd)
{
struct wpabuf *buf = wpabuf_alloc(12);
@@ -841,11 +1016,12 @@
static int hostapd_mld_validate_assoc_info(struct hostapd_data *hapd,
- struct mld_info *info)
+ struct sta_info *sta)
{
u8 i, link_id;
+ struct mld_info *info = &sta->mld_info;
- if (!info->mld_sta) {
+ if (!ap_sta_is_mld(hapd, sta)) {
wpa_printf(MSG_DEBUG, "MLD: Not a non-AP MLD");
return 0;
}
@@ -894,6 +1070,85 @@
}
+int hostapd_process_ml_assoc_req_addr(struct hostapd_data *hapd,
+ const u8 *basic_mle, size_t basic_mle_len,
+ u8 *mld_addr)
+{
+ struct wpabuf *mlbuf = ieee802_11_defrag(basic_mle, basic_mle_len,
+ true);
+ struct ieee80211_eht_ml *ml;
+ struct eht_ml_basic_common_info *common_info;
+ size_t ml_len, common_info_len;
+ int ret = -1;
+ u16 ml_control;
+
+ if (!mlbuf)
+ return WLAN_STATUS_SUCCESS;
+
+ ml = (struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
+ ml_len = wpabuf_len(mlbuf);
+
+ if (ml_len < sizeof(*ml))
+ goto out;
+
+ ml_control = le_to_host16(ml->ml_control);
+ if ((ml_control & MULTI_LINK_CONTROL_TYPE_MASK) !=
+ MULTI_LINK_CONTROL_TYPE_BASIC) {
+ wpa_printf(MSG_DEBUG, "MLD: Invalid ML type=%u",
+ ml_control & MULTI_LINK_CONTROL_TYPE_MASK);
+ goto out;
+ }
+
+ /* Common Info Length and MLD MAC Address must always be present */
+ common_info_len = 1 + ETH_ALEN;
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_LINK_ID) {
+ wpa_printf(MSG_DEBUG, "MLD: Link ID Info not expected");
+ goto out;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: BSS Parameters Change Count not expected");
+ goto out;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MSD_INFO) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Medium Synchronization Delay Information not expected");
+ goto out;
+ }
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_EML_CAPA)
+ common_info_len += 2;
+
+ if (ml_control & BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA)
+ common_info_len += 2;
+
+ if (sizeof(*ml) + common_info_len > ml_len) {
+ wpa_printf(MSG_DEBUG, "MLD: Not enough bytes for common info");
+ goto out;
+ }
+
+ common_info = (struct eht_ml_basic_common_info *) ml->variable;
+
+ /* Common information length includes the length octet */
+ if (common_info->len != common_info_len) {
+ wpa_printf(MSG_DEBUG,
+ "MLD: Invalid common info len=%u", common_info->len);
+ goto out;
+ }
+
+ /* Get the MLD MAC Address */
+ os_memcpy(mld_addr, common_info->mld_addr, ETH_ALEN);
+ ret = 0;
+
+out:
+ wpabuf_free(mlbuf);
+ return ret;
+}
+
+
u16 hostapd_process_ml_assoc_req(struct hostapd_data *hapd,
struct ieee802_11_elems *elems,
struct sta_info *sta)
@@ -908,7 +1163,7 @@
int ret = -1;
u16 ml_control;
- mlbuf = ieee802_11_defrag_mle(elems, MULTI_LINK_CONTROL_TYPE_BASIC);
+ mlbuf = ieee802_11_defrag(elems->basic_mle, elems->basic_mle_len, true);
if (!mlbuf)
return WLAN_STATUS_SUCCESS;
@@ -990,8 +1245,8 @@
info->common_info.eml_capa, info->common_info.mld_capa);
/* Check the MLD MAC Address */
- if (os_memcmp(info->common_info.mld_addr, common_info->mld_addr,
- ETH_ALEN) != 0) {
+ if (!ether_addr_equal(info->common_info.mld_addr,
+ common_info->mld_addr)) {
wpa_printf(MSG_DEBUG,
"MLD: MLD address mismatch between authentication ("
MACSTR ") and association (" MACSTR ")",
@@ -1000,7 +1255,7 @@
goto out;
}
- info->links[hapd->mld_link_id].valid = true;
+ info->links[hapd->mld_link_id].valid = 1;
/* Parse the link info field */
ml_len -= sizeof(*ml) + common_info_len;
@@ -1031,9 +1286,11 @@
if (*pos != MULTI_LINK_SUB_ELEM_ID_PER_STA_PROFILE) {
wpa_printf(MSG_DEBUG,
- "MLD: Unexpected Multi-Link element subelement ID=%u",
+ "MLD: Skip unknown Multi-Link element subelement ID=%u",
*pos);
- goto out;
+ pos += 2 + sub_elem_len;
+ ml_len -= 2 + sub_elem_len;
+ continue;
}
/* Skip the subelement ID and the length */
@@ -1138,7 +1395,7 @@
goto out;
}
- ret = hostapd_mld_validate_assoc_info(hapd, info);
+ ret = hostapd_mld_validate_assoc_info(hapd, sta);
out:
wpabuf_free(mlbuf);
if (ret) {
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 548a448..4b693a7 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -254,16 +254,15 @@
control = 3;
else
control = center_idx_to_bw_6ghz(seg0);
- if (hapd->iconf->he_6ghz_reg_pwr_type == 1)
- control |= HE_6GHZ_STANDARD_POWER_AP <<
- HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
- else
- control |= HE_6GHZ_INDOOR_AP <<
- HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
+
+ control |= hapd->iconf->he_6ghz_reg_pwr_type <<
+ HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
+
*pos++ = control;
/* Channel Center Freq Seg0/Seg1 */
- if (oper_chwidth == 2) {
+ if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
+ oper_chwidth == CONF_OPER_CHWIDTH_320MHZ) {
/*
* Seg 0 indicates the channel center frequency index of
* the 160 MHz channel.
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 31dfb62..0c38483 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -1,6 +1,6 @@
/*
* hostapd / IEEE 802.11 Management
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2024, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -21,16 +21,25 @@
#include "ieee802_11.h"
+static u8 * hostapd_eid_timeout_interval(u8 *pos, u8 type, u32 value)
+{
+ *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+ *pos++ = 5;
+ *pos++ = type;
+ WPA_PUT_LE32(pos, value);
+ pos += 4;
+
+ return pos;
+}
+
+
u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
struct sta_info *sta, u8 *eid)
{
- u8 *pos = eid;
u32 timeout, tu;
struct os_reltime now, passed;
+ u8 type = WLAN_TIMEOUT_ASSOC_COMEBACK;
- *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
- *pos++ = 5;
- *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
os_get_reltime(&now);
os_reltime_sub(&now, &sta->sa_query_start, &passed);
tu = (passed.sec * 1000000 + passed.usec) / 1024;
@@ -40,10 +49,12 @@
timeout = 0;
if (timeout < hapd->conf->assoc_sa_query_max_timeout)
timeout++; /* add some extra time for local timers */
- WPA_PUT_LE32(pos, timeout);
- pos += 4;
- return pos;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->test_assoc_comeback_type != -1)
+ type = hapd->conf->test_assoc_comeback_type;
+#endif /* CONFIG_TESTING_OPTIONS */
+ return hostapd_eid_timeout_interval(eid, type, timeout);
}
@@ -51,13 +62,14 @@
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
const u8 *addr, const u8 *trans_id)
{
-#ifdef CONFIG_OCV
- struct sta_info *sta;
-#endif /* CONFIG_OCV */
+#if defined(CONFIG_OCV) || defined(CONFIG_IEEE80211BE)
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+#endif /* CONFIG_OCV || CONFIG_IEEE80211BE */
struct ieee80211_mgmt *mgmt;
u8 *oci_ie = NULL;
u8 oci_ie_len = 0;
u8 *end;
+ const u8 *own_addr = hapd->own_addr;
wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
MACSTR, MAC2STR(addr));
@@ -65,7 +77,6 @@
trans_id, WLAN_SA_QUERY_TR_ID_LEN);
#ifdef CONFIG_OCV
- sta = ap_get_sta(hapd, addr);
if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
struct wpa_channel_info ci;
@@ -108,11 +119,16 @@
return;
}
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta))
+ own_addr = hapd->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_SA_QUERY;
mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id,
@@ -141,6 +157,7 @@
u8 *oci_ie = NULL;
u8 oci_ie_len = 0;
u8 *end;
+ const u8 *own_addr = hapd->own_addr;
wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
MACSTR, MAC2STR(sa));
@@ -200,11 +217,16 @@
wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
MACSTR, MAC2STR(sa));
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta))
+ own_addr = hapd->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(resp->da, sa, ETH_ALEN);
- os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(resp->sa, own_addr, ETH_ALEN);
+ os_memcpy(resp->bssid, own_addr, ETH_ALEN);
resp->u.action.category = WLAN_ACTION_SA_QUERY;
resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id,
@@ -1137,3 +1159,44 @@
return WLAN_STATUS_SUCCESS;
}
+
+
+struct sta_info * hostapd_ml_get_assoc_sta(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct hostapd_data **assoc_hapd)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *other_hapd = NULL;
+ struct sta_info *tmp_sta;
+
+ if (!ap_sta_is_mld(hapd, sta))
+ return NULL;
+
+ *assoc_hapd = hapd;
+
+ /* The station is the one on which the association was performed */
+ if (sta->mld_assoc_link_id == hapd->mld_link_id)
+ return sta;
+
+ other_hapd = hostapd_mld_get_link_bss(hapd, sta->mld_assoc_link_id);
+ if (!other_hapd) {
+ wpa_printf(MSG_DEBUG, "MLD: No link match for link_id=%u",
+ sta->mld_assoc_link_id);
+ return sta;
+ }
+
+ /*
+ * Iterate over the stations and find the one with the matching link ID
+ * and association ID.
+ */
+ for (tmp_sta = other_hapd->sta_list; tmp_sta; tmp_sta = tmp_sta->next) {
+ if (tmp_sta->mld_assoc_link_id == sta->mld_assoc_link_id &&
+ tmp_sta->aid == sta->aid) {
+ *assoc_hapd = other_hapd;
+ return tmp_sta;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
+ return sta;
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 052231e..f13c60a 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -114,12 +114,15 @@
bool authorized, bool mld)
{
int res;
+ bool update;
if (sta->flags & WLAN_STA_PREAUTH)
return;
- ap_sta_set_authorized(hapd, sta, authorized);
+ update = ap_sta_set_authorized_flag(hapd, sta, authorized);
res = hostapd_set_authorized(hapd, sta, authorized);
+ if (update)
+ ap_sta_set_authorized_event(hapd, sta, authorized);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_DEBUG, "%sauthorizing port",
authorized ? "" : "un");
diff --git a/src/ap/nan_usd_ap.c b/src/ap/nan_usd_ap.c
new file mode 100644
index 0000000..52a967a
--- /dev/null
+++ b/src/ap/nan_usd_ap.c
@@ -0,0 +1,267 @@
+/*
+ * NAN unsynchronized service discovery (USD)
+ * Copyright (c) 2024, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "common/nan_de.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "nan_usd_ap.h"
+
+
+static int hostapd_nan_de_tx(void *ctx, unsigned int freq,
+ unsigned int wait_time,
+ const u8 *dst, const u8 *src, const u8 *bssid,
+ const struct wpabuf *buf)
+{
+ struct hostapd_data *hapd = ctx;
+
+ wpa_printf(MSG_DEBUG, "NAN: TX NAN SDF A1=" MACSTR " A2=" MACSTR
+ " A3=" MACSTR " len=%zu",
+ MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
+ wpabuf_len(buf));
+
+ /* TODO: Force use of OFDM */
+ return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
+ wpabuf_head(buf), wpabuf_len(buf));
+}
+
+
+static int hostapd_nan_de_listen(void *ctx, unsigned int freq,
+ unsigned int duration)
+{
+ return 0;
+}
+
+
+static void
+hostapd_nan_de_discovery_result(void *ctx, int subscribe_id,
+ enum nan_service_protocol_type srv_proto_type,
+ const u8 *ssi, size_t ssi_len,
+ int peer_publish_id, const u8 *peer_addr,
+ bool fsd, bool fsd_gas)
+{
+ struct hostapd_data *hapd = ctx;
+ char *ssi_hex;
+
+ ssi_hex = os_zalloc(2 * ssi_len + 1);
+ if (!ssi_hex)
+ return;
+ if (ssi)
+ wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_DISCOVERY_RESULT
+ "subscribe_id=%d publish_id=%d address=" MACSTR
+ " fsd=%d fsd_gas=%d srv_proto_type=%u ssi=%s",
+ subscribe_id, peer_publish_id, MAC2STR(peer_addr),
+ fsd, fsd_gas, srv_proto_type, ssi_hex);
+ os_free(ssi_hex);
+}
+
+
+static void
+hostapd_nan_de_replied(void *ctx, int publish_id, const u8 *peer_addr,
+ int peer_subscribe_id,
+ enum nan_service_protocol_type srv_proto_type,
+ const u8 *ssi, size_t ssi_len)
+{
+ struct hostapd_data *hapd = ctx;
+ char *ssi_hex;
+
+ ssi_hex = os_zalloc(2 * ssi_len + 1);
+ if (!ssi_hex)
+ return;
+ if (ssi)
+ wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_REPLIED
+ "publish_id=%d address=" MACSTR
+ " subscribe_id=%d srv_proto_type=%u ssi=%s",
+ publish_id, MAC2STR(peer_addr), peer_subscribe_id,
+ srv_proto_type, ssi_hex);
+ os_free(ssi_hex);
+}
+
+
+static const char * nan_reason_txt(enum nan_de_reason reason)
+{
+ switch (reason) {
+ case NAN_DE_REASON_TIMEOUT:
+ return "timeout";
+ case NAN_DE_REASON_USER_REQUEST:
+ return "user-request";
+ case NAN_DE_REASON_FAILURE:
+ return "failure";
+ }
+
+ return "unknown";
+}
+
+
+static void hostapd_nan_de_publish_terminated(void *ctx, int publish_id,
+ enum nan_de_reason reason)
+{
+ struct hostapd_data *hapd = ctx;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_PUBLISH_TERMINATED
+ "publish_id=%d reason=%s",
+ publish_id, nan_reason_txt(reason));
+}
+
+
+static void hostapd_nan_de_subscribe_terminated(void *ctx, int subscribe_id,
+ enum nan_de_reason reason)
+{
+ struct hostapd_data *hapd = ctx;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_SUBSCRIBE_TERMINATED
+ "subscribe_id=%d reason=%s",
+ subscribe_id, nan_reason_txt(reason));
+}
+
+
+static void hostapd_nan_de_receive(void *ctx, int id, int peer_instance_id,
+ const u8 *ssi, size_t ssi_len,
+ const u8 *peer_addr)
+{
+ struct hostapd_data *hapd = ctx;
+ char *ssi_hex;
+
+ ssi_hex = os_zalloc(2 * ssi_len + 1);
+ if (!ssi_hex)
+ return;
+ if (ssi)
+ wpa_snprintf_hex(ssi_hex, 2 * ssi_len + 1, ssi, ssi_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, NAN_RECEIVE
+ "id=%d peer_instance_id=%d address=" MACSTR " ssi=%s",
+ id, peer_instance_id, MAC2STR(peer_addr), ssi_hex);
+ os_free(ssi_hex);
+}
+
+
+int hostapd_nan_usd_init(struct hostapd_data *hapd)
+{
+ struct nan_callbacks cb;
+
+ os_memset(&cb, 0, sizeof(cb));
+ cb.ctx = hapd;
+ cb.tx = hostapd_nan_de_tx;
+ cb.listen = hostapd_nan_de_listen;
+ cb.discovery_result = hostapd_nan_de_discovery_result;
+ cb.replied = hostapd_nan_de_replied;
+ cb.publish_terminated = hostapd_nan_de_publish_terminated;
+ cb.subscribe_terminated = hostapd_nan_de_subscribe_terminated;
+ cb.receive = hostapd_nan_de_receive;
+
+ hapd->nan_de = nan_de_init(hapd->own_addr, true, &cb);
+ if (!hapd->nan_de)
+ return -1;
+ return 0;
+}
+
+
+void hostapd_nan_usd_deinit(struct hostapd_data *hapd)
+{
+ nan_de_deinit(hapd->nan_de);
+ hapd->nan_de = NULL;
+}
+
+
+void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
+ unsigned int freq, const u8 *buf, size_t len)
+{
+ if (!hapd->nan_de)
+ return;
+ nan_de_rx_sdf(hapd->nan_de, src, freq, buf, len);
+}
+
+
+void hostapd_nan_usd_flush(struct hostapd_data *hapd)
+{
+ if (!hapd->nan_de)
+ return;
+ nan_de_flush(hapd->nan_de);
+}
+
+
+int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name,
+ enum nan_service_protocol_type srv_proto_type,
+ const struct wpabuf *ssi,
+ struct nan_publish_params *params)
+{
+ int publish_id;
+ struct wpabuf *elems = NULL;
+
+ if (!hapd->nan_de)
+ return -1;
+
+ publish_id = nan_de_publish(hapd->nan_de, service_name, srv_proto_type,
+ ssi, elems, params);
+ wpabuf_free(elems);
+ return publish_id;
+}
+
+
+void hostapd_nan_usd_cancel_publish(struct hostapd_data *hapd, int publish_id)
+{
+ if (!hapd->nan_de)
+ return;
+ nan_de_cancel_publish(hapd->nan_de, publish_id);
+}
+
+
+int hostapd_nan_usd_update_publish(struct hostapd_data *hapd, int publish_id,
+ const struct wpabuf *ssi)
+{
+ int ret;
+
+ if (!hapd->nan_de)
+ return -1;
+ ret = nan_de_update_publish(hapd->nan_de, publish_id, ssi);
+ return ret;
+}
+
+
+int hostapd_nan_usd_subscribe(struct hostapd_data *hapd,
+ const char *service_name,
+ enum nan_service_protocol_type srv_proto_type,
+ const struct wpabuf *ssi,
+ struct nan_subscribe_params *params)
+{
+ int subscribe_id;
+ struct wpabuf *elems = NULL;
+
+ if (!hapd->nan_de)
+ return -1;
+
+ subscribe_id = nan_de_subscribe(hapd->nan_de, service_name,
+ srv_proto_type, ssi, elems, params);
+ wpabuf_free(elems);
+ return subscribe_id;
+}
+
+
+void hostapd_nan_usd_cancel_subscribe(struct hostapd_data *hapd,
+ int subscribe_id)
+{
+ if (!hapd->nan_de)
+ return;
+ nan_de_cancel_subscribe(hapd->nan_de, subscribe_id);
+}
+
+
+int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle,
+ const struct wpabuf *ssi,
+ const struct wpabuf *elems,
+ const u8 *peer_addr, u8 req_instance_id)
+{
+ if (!hapd->nan_de)
+ return -1;
+ return nan_de_transmit(hapd->nan_de, handle, ssi, elems, peer_addr,
+ req_instance_id);
+}
diff --git a/src/ap/nan_usd_ap.h b/src/ap/nan_usd_ap.h
new file mode 100644
index 0000000..58ff5fc
--- /dev/null
+++ b/src/ap/nan_usd_ap.h
@@ -0,0 +1,46 @@
+/*
+ * NAN unsynchronized service discovery (USD)
+ * Copyright (c) 2024, Qualcomm Innovation Center, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NAN_USD_AP_H
+#define NAN_USD_AP_H
+
+struct nan_subscribe_params;
+struct nan_publish_params;
+enum nan_service_protocol_type;
+
+int hostapd_nan_usd_init(struct hostapd_data *hapd);
+void hostapd_nan_usd_deinit(struct hostapd_data *hapd);
+void hostapd_nan_usd_rx_sdf(struct hostapd_data *hapd, const u8 *src,
+ unsigned int freq, const u8 *buf, size_t len);
+void hostapd_nan_usd_flush(struct hostapd_data *hapd);
+int hostapd_nan_usd_publish(struct hostapd_data *hapd, const char *service_name,
+ enum nan_service_protocol_type srv_proto_type,
+ const struct wpabuf *ssi,
+ struct nan_publish_params *params);
+void hostapd_nan_usd_cancel_publish(struct hostapd_data *hapd, int publish_id);
+int hostapd_nan_usd_update_publish(struct hostapd_data *hapd, int publish_id,
+ const struct wpabuf *ssi);
+int hostapd_nan_usd_subscribe(struct hostapd_data *hapd,
+ const char *service_name,
+ enum nan_service_protocol_type srv_proto_type,
+ const struct wpabuf *ssi,
+ struct nan_subscribe_params *params);
+void hostapd_nan_usd_cancel_subscribe(struct hostapd_data *hapd,
+ int subscribe_id);
+int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle,
+ const struct wpabuf *ssi,
+ const struct wpabuf *elems,
+ const u8 *peer_addr, u8 req_instance_id);
+void hostapd_nan_usd_remain_on_channel_cb(struct hostapd_data *hapd,
+ unsigned int freq,
+ unsigned int duration);
+void hostapd_nan_usd_cancel_remain_on_channel_cb(struct hostapd_data *hapd,
+ unsigned int freq);
+void hostapd_nan_usd_tx_wait_expire(struct hostapd_data *hapd);
+
+#endif /* NAN_USD_AP_H */
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index 5b276e8..2a25ae2 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -24,7 +24,7 @@
dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
list) {
- if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(bssid, nr->bssid) &&
(!ssid ||
(ssid->ssid_len == nr->ssid.ssid_len &&
os_memcmp(ssid->ssid, nr->ssid.ssid,
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index ee4232f..2fce838 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -487,14 +487,14 @@
for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry;
entry = entry->hnext) {
if ((spa == NULL ||
- os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
+ ether_addr_equal(entry->spa, spa)) &&
os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
return entry;
}
} else {
for (entry = pmksa->pmksa; entry; entry = entry->next) {
if (spa == NULL ||
- os_memcmp(entry->spa, spa, ETH_ALEN) == 0)
+ ether_addr_equal(entry->spa, spa))
return entry;
}
}
@@ -521,7 +521,7 @@
u8 new_pmkid[PMKID_LEN];
for (entry = pmksa->pmksa; entry; entry = entry->next) {
- if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
+ if (!ether_addr_equal(entry->spa, spa))
continue;
if (wpa_key_mgmt_sae(entry->akmp) ||
wpa_key_mgmt_fils(entry->akmp)) {
@@ -575,7 +575,7 @@
int match = 0;
if (attr->sta_addr) {
- if (os_memcmp(attr->sta_addr, entry->spa, ETH_ALEN) != 0)
+ if (!ether_addr_equal(attr->sta_addr, entry->spa))
return 0;
match++;
}
@@ -717,7 +717,7 @@
* <BSSID> <PMKID> <PMK> <expiration in seconds>
*/
for (entry = pmksa->pmksa; entry; entry = entry->next) {
- if (addr && os_memcmp(entry->spa, addr, ETH_ALEN) != 0)
+ if (addr && !ether_addr_equal(entry->spa, addr))
continue;
ret = os_snprintf(pos, end - pos, MACSTR " ",
diff --git a/src/ap/preauth_auth.c b/src/ap/preauth_auth.c
index 3284a10..cb225c6 100644
--- a/src/ap/preauth_auth.c
+++ b/src/ap/preauth_auth.c
@@ -58,7 +58,7 @@
ethhdr = (struct l2_ethhdr *) buf;
hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
- if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(ethhdr->h_dest, hapd->own_addr)) {
wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address "
MACSTR, MAC2STR(ethhdr->h_dest));
return;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index a00f896..2178d65 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -92,7 +92,7 @@
if (p2p_dev_addr == NULL)
continue;
- if (os_memcmp(p2p_dev_addr, addr, ETH_ALEN) == 0)
+ if (ether_addr_equal(p2p_dev_addr, addr))
return sta;
}
@@ -140,7 +140,7 @@
}
while (s->hnext != NULL &&
- os_memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0)
+ !ether_addr_equal(s->hnext->addr, sta->addr))
s = s->hnext;
if (s->hnext != NULL)
s->hnext = s->hnext->hnext;
@@ -303,7 +303,7 @@
ieee802_1x_free_station(hapd, sta);
#ifdef CONFIG_IEEE80211BE
- if (!hapd->conf->mld_ap || !sta->mld_info.mld_sta ||
+ if (!ap_sta_is_mld(hapd, sta) ||
hapd->mld_link_id == sta->mld_assoc_link_id)
wpa_auth_sta_deinit(sta->wpa_sm);
#else
@@ -350,6 +350,7 @@
#ifdef CONFIG_INTERWORKING
if (sta->gas_dialog) {
int i;
+
for (i = 0; i < GAS_DIALOG_MAX; i++)
gas_serv_dialog_clear(&sta->gas_dialog[i]);
os_free(sta->gas_dialog);
@@ -420,6 +421,10 @@
os_free(sta->ifname_wds);
+#ifdef CONFIG_IEEE80211BE
+ ap_sta_free_sta_profile(&sta->mld_info);
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_TESTING_OPTIONS
os_free(sta->sae_postponed_commit);
forced_memzero(sta->last_tk, WPA_TK_MAX_LEN);
@@ -846,32 +851,20 @@
}
-void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
- u16 reason)
+static void ap_sta_disconnect_common(struct hostapd_data *hapd,
+ struct sta_info *sta, unsigned int timeout)
{
- wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
- hapd->conf->iface, MAC2STR(sta->addr));
sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
- if (hapd->iface->current_mode &&
- hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
- /* Skip deauthentication in DMG/IEEE 802.11ad */
- sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
- WLAN_STA_ASSOC_REQ_OK);
- sta->timeout_next = STA_REMOVE;
- } else {
- sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
- sta->timeout_next = STA_DEAUTH;
- }
+
ap_sta_set_authorized(hapd, sta, 0);
hostapd_set_sta_flags(hapd, sta);
- wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
- "for " MACSTR " (%d seconds - "
- "AP_MAX_INACTIVITY_AFTER_DISASSOC)",
- __func__, MAC2STR(sta->addr),
- AP_MAX_INACTIVITY_AFTER_DISASSOC);
+
+ wpa_printf(MSG_DEBUG,
+ "reschedule ap_handle_timer timeout (%u sec) for " MACSTR,
+ MAC2STR(sta->addr), timeout);
+
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
- eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
- ap_handle_timer, hapd, sta);
+ eloop_register_timeout(timeout, 0, ap_handle_timer, hapd, sta);
accounting_sta_stop(hapd, sta);
ieee802_1x_free_station(hapd, sta);
#ifdef CONFIG_IEEE80211BE
@@ -883,6 +876,27 @@
#endif /* CONFIG_IEEE80211BE */
sta->wpa_sm = NULL;
+}
+
+
+static void ap_sta_handle_disassociate(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason)
+{
+ wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
+ hapd->conf->iface, MAC2STR(sta->addr));
+
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+ /* Skip deauthentication in DMG/IEEE 802.11ad */
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+ WLAN_STA_ASSOC_REQ_OK);
+ sta->timeout_next = STA_REMOVE;
+ } else {
+ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+ sta->timeout_next = STA_DEAUTH;
+ }
+
+ ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DISASSOC);
sta->disassoc_reason = reason;
sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
@@ -905,8 +919,8 @@
}
-void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
- u16 reason)
+static void ap_sta_handle_deauthenticate(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason)
{
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
@@ -918,21 +932,11 @@
wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
- sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+
sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
- ap_sta_set_authorized(hapd, sta, 0);
- hostapd_set_sta_flags(hapd, sta);
+
sta->timeout_next = STA_REMOVE;
- wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
- "for " MACSTR " (%d seconds - "
- "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
- __func__, MAC2STR(sta->addr),
- AP_MAX_INACTIVITY_AFTER_DEAUTH);
- eloop_cancel_timeout(ap_handle_timer, hapd, sta);
- eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
- ap_handle_timer, hapd, sta);
- accounting_sta_stop(hapd, sta);
- ieee802_1x_free_station(hapd, sta);
+ ap_sta_disconnect_common(hapd, sta, AP_MAX_INACTIVITY_AFTER_DEAUTH);
sta->deauth_reason = reason;
sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
@@ -943,6 +947,105 @@
}
+static bool ap_sta_ml_disconnect(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 reason,
+ bool disassoc)
+{
+#ifdef CONFIG_IEEE80211BE
+ struct hostapd_data *assoc_hapd, *tmp_hapd;
+ struct sta_info *assoc_sta;
+ unsigned int i, link_id;
+ struct hapd_interfaces *interfaces;
+
+ if (!hostapd_is_mld_ap(hapd))
+ return false;
+
+ /*
+ * Get the station on which the association was performed, as it holds
+ * the information about all the other links.
+ */
+ assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, &assoc_hapd);
+ if (!assoc_sta)
+ return false;
+ interfaces = assoc_hapd->iface->interfaces;
+
+ for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
+ for (i = 0; i < interfaces->count; i++) {
+ struct sta_info *tmp_sta;
+
+ if (!assoc_sta->mld_info.links[link_id].valid)
+ continue;
+
+ tmp_hapd = interfaces->iface[i]->bss[0];
+
+ if (!tmp_hapd->conf->mld_ap ||
+ assoc_hapd->conf->mld_id != tmp_hapd->conf->mld_id)
+ continue;
+
+ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+ tmp_sta = tmp_sta->next) {
+ /*
+ * Handle the station on which the association
+ * was done only after all other link station
+ * are removed. Since there is a only a single
+ * station per hapd with the same association
+ * link simply break;
+ */
+ 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)
+ ap_sta_handle_disassociate(tmp_hapd,
+ tmp_sta,
+ reason);
+ else
+ ap_sta_handle_deauthenticate(tmp_hapd,
+ tmp_sta,
+ reason);
+
+ break;
+ }
+ }
+ }
+
+ /* Disconnect the station on which the association was performed. */
+ if (disassoc)
+ ap_sta_handle_disassociate(assoc_hapd, assoc_sta, reason);
+ else
+ ap_sta_handle_deauthenticate(assoc_hapd, assoc_sta, reason);
+
+ return true;
+#else /* CONFIG_IEEE80211BE */
+ return false;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+
+void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason)
+{
+ if (ap_sta_ml_disconnect(hapd, sta, reason, true))
+ return;
+
+ ap_sta_handle_disassociate(hapd, sta, reason);
+}
+
+
+void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
+ u16 reason)
+{
+ if (ap_sta_ml_disconnect(hapd, sta, reason, false))
+ return;
+
+ ap_sta_handle_deauthenticate(hapd, sta, reason);
+}
+
+
#ifdef CONFIG_WPS
int ap_sta_wps_cancel(struct hostapd_data *hapd,
struct sta_info *sta, void *ctx)
@@ -1292,25 +1395,17 @@
}
-void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
- int authorized)
+bool ap_sta_set_authorized_flag(struct hostapd_data *hapd, struct sta_info *sta,
+ int authorized)
{
- const u8 *dev_addr = NULL;
- char buf[100];
-#ifdef CONFIG_P2P
- u8 addr[ETH_ALEN];
- u8 ip_addr_buf[4];
-#endif /* CONFIG_P2P */
- u8 *ip_ptr = NULL;
-
if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
- return;
+ return false;
if (authorized) {
int mld_assoc_link_id = -1;
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
if (sta->mld_assoc_link_id == hapd->mld_link_id)
mld_assoc_link_id = sta->mld_assoc_link_id;
else
@@ -1325,6 +1420,21 @@
sta->flags &= ~WLAN_STA_AUTHORIZED;
}
+ return true;
+}
+
+
+void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized)
+{
+ const u8 *dev_addr = NULL;
+ char buf[100];
+#ifdef CONFIG_P2P
+ u8 addr[ETH_ALEN];
+ u8 ip_addr_buf[4];
+#endif /* CONFIG_P2P */
+ u8 *ip_ptr = NULL;
+
#ifdef CONFIG_P2P
if (hapd->p2p_group == NULL) {
if (sta->p2p_ie != NULL &&
@@ -1413,6 +1523,15 @@
}
+void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
+ int authorized)
+{
+ if (!ap_sta_set_authorized_flag(hapd, sta, authorized))
+ return;
+ ap_sta_set_authorized_event(hapd, sta, authorized);
+}
+
+
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 reason)
{
@@ -1604,6 +1723,34 @@
}
+#ifdef CONFIG_IEEE80211BE
+static void ap_sta_remove_link_sta(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct hostapd_data *tmp_hapd;
+ unsigned int i, j;
+
+ for_each_mld_link(tmp_hapd, i, j, hapd->iface->interfaces,
+ hapd->conf->mld_id) {
+ struct sta_info *tmp_sta;
+
+ if (hapd == tmp_hapd)
+ continue;
+
+ for (tmp_sta = tmp_hapd->sta_list; tmp_sta;
+ tmp_sta = tmp_sta->next) {
+ if (tmp_sta == sta ||
+ !ether_addr_equal(tmp_sta->addr, sta->addr))
+ continue;
+
+ ap_free_sta(tmp_hapd, tmp_sta);
+ break;
+ }
+ }
+}
+#endif /* CONFIG_IEEE80211BE */
+
+
int ap_sta_re_add(struct hostapd_data *hapd, struct sta_info *sta)
{
const u8 *mld_link_addr = NULL;
@@ -1618,11 +1765,17 @@
*/
#ifdef CONFIG_IEEE80211BE
- if (hapd->conf->mld_ap && sta->mld_info.mld_sta) {
+ if (ap_sta_is_mld(hapd, sta)) {
u8 mld_link_id = hapd->mld_link_id;
mld_link_sta = sta->mld_assoc_link_id != mld_link_id;
mld_link_addr = sta->mld_info.links[mld_link_id].peer_addr;
+
+ /*
+ * In case the AP is affiliated with an AP MLD, we need to
+ * remove the station from all relevant links/APs.
+ */
+ ap_sta_remove_link_sta(hapd, sta);
}
#endif /* CONFIG_IEEE80211BE */
@@ -1646,3 +1799,19 @@
sta->added_unassoc = 1;
return 0;
}
+
+
+#ifdef CONFIG_IEEE80211BE
+void ap_sta_free_sta_profile(struct mld_info *info)
+{
+ int i;
+
+ if (!info)
+ return;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ os_free(info->links[i].resp_sta_profile);
+ info->links[i].resp_sta_profile = NULL;
+ }
+}
+#endif /* CONFIG_IEEE80211BE */
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index e2b9dde..b136ff7 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -17,6 +17,7 @@
#include "common/sae.h"
#include "crypto/sha384.h"
#include "pasn/pasn_common.h"
+#include "hostapd.h"
/* STA flags */
#define WLAN_STA_AUTH BIT(0)
@@ -81,18 +82,18 @@
} common_info;
struct mld_link_info {
- u8 valid;
+ u8 valid:1;
+ u8 nstr_bitmap_len:2;
u8 local_addr[ETH_ALEN];
u8 peer_addr[ETH_ALEN];
- size_t nstr_bitmap_len;
u8 nstr_bitmap[2];
u16 capability;
u16 status;
- size_t resp_sta_profile_len;
- u8 resp_sta_profile[EHT_ML_MAX_STA_PROF_LEN];
+ u16 resp_sta_profile_len;
+ u8 *resp_sta_profile;
const u8 *rsne, *rsnxe;
} links[MAX_NUM_MLD_LINKS];
@@ -393,6 +394,10 @@
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 reason);
+bool ap_sta_set_authorized_flag(struct hostapd_data *hapd, struct sta_info *sta,
+ int authorized);
+void ap_sta_set_authorized_event(struct hostapd_data *hapd,
+ struct sta_info *sta, int authorized);
void ap_sta_set_authorized(struct hostapd_data *hapd,
struct sta_info *sta, int authorized);
static inline int ap_sta_is_authorized(struct sta_info *sta)
@@ -415,4 +420,24 @@
void ap_free_sta_pasn(struct hostapd_data *hapd, struct sta_info *sta);
+static inline bool ap_sta_is_mld(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+#ifdef CONFIG_IEEE80211BE
+ return hapd->conf->mld_ap && sta && sta->mld_info.mld_sta;
+#else /* CONFIG_IEEE80211BE */
+ return false;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+static inline void ap_sta_set_mld(struct sta_info *sta, bool mld)
+{
+#ifdef CONFIG_IEEE80211BE
+ if (sta)
+ sta->mld_info.mld_sta = mld;
+#endif /* CONFIG_IEEE80211BE */
+}
+
+void ap_sta_free_sta_profile(struct mld_info *info);
+
#endif /* STA_INFO_H */
diff --git a/src/ap/wmm.c b/src/ap/wmm.c
index 9ebb01e..dad768e 100644
--- a/src/ap/wmm.c
+++ b/src/ap/wmm.c
@@ -20,13 +20,6 @@
#include "ap_drv_ops.h"
#include "wmm.h"
-#ifndef MIN
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
-#endif
-#ifndef MAX
-#define MAX(a, b) (((a) > (b)) ? (a) : (b))
-#endif
-
static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
{
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 153ee40..b77e21b 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -44,6 +44,20 @@
}
+static const u8 * wnm_ap_get_own_addr(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ const u8 *own_addr = hapd->own_addr;
+
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->conf->mld_ap && (!sta || ap_sta_is_mld(hapd, sta)))
+ own_addr = hapd->mld_addr;
+#endif /* CONFIG_IEEE80211BE */
+
+ return own_addr;
+}
+
+
/* MLME-SLEEPMODE.response */
static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
const u8 *addr, u8 dialog_token,
@@ -63,6 +77,7 @@
struct sta_info *sta;
enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ?
WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE;
+ const u8 *own_addr;
sta = ap_get_sta(hapd, addr);
if (sta == NULL) {
@@ -143,9 +158,12 @@
res = -1;
goto fail;
}
+
+ own_addr = wnm_ap_get_own_addr(hapd, sta);
+
os_memcpy(mgmt->da, addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
mgmt->u.action.category = WLAN_ACTION_WNM;
@@ -366,6 +384,8 @@
u8 dialog_token)
{
struct ieee80211_mgmt *mgmt;
+ const u8 *own_addr;
+ struct sta_info *sta;
size_t len;
u8 *pos;
int res;
@@ -373,9 +393,13 @@
mgmt = os_zalloc(sizeof(*mgmt));
if (mgmt == NULL)
return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ own_addr = wnm_ap_get_own_addr(hapd, sta);
+
os_memcpy(mgmt->da, addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
mgmt->u.action.category = WLAN_ACTION_WNM;
@@ -821,14 +845,15 @@
{
u8 buf[1000], *pos;
struct ieee80211_mgmt *mgmt;
+ const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
os_memset(buf, 0, sizeof(buf));
mgmt = (struct ieee80211_mgmt *) buf;
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
mgmt->u.action.u.bss_tm_req.dialog_token = 1;
@@ -887,14 +912,15 @@
u8 buf[1000], *pos;
struct ieee80211_mgmt *mgmt;
size_t url_len;
+ const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
os_memset(buf, 0, sizeof(buf));
mgmt = (struct ieee80211_mgmt *) buf;
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
mgmt->u.action.u.bss_tm_req.dialog_token = 1;
@@ -939,6 +965,7 @@
u8 *buf, *pos;
struct ieee80211_mgmt *mgmt;
size_t url_len;
+ const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
MACSTR
@@ -952,8 +979,8 @@
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
@@ -1002,6 +1029,26 @@
os_free(buf);
if (disassoc_timer) {
+#ifdef CONFIG_IEEE80211BE
+ if (ap_sta_is_mld(hapd, sta)) {
+ int i;
+ unsigned int links = 0;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (sta->mld_info.links[i].valid)
+ links++;
+ }
+
+ if (links > 1) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Only terminating one link - other links remains associated for "
+ MACSTR,
+ MAC2STR(sta->mld_info.common_info.mld_addr));
+ return 0;
+ }
+ }
+#endif /* CONFIG_IEEE80211BE */
+
/* send disassociation frame after time-out */
set_disassoc_timer(hapd, sta, disassoc_timer);
}
@@ -1016,6 +1063,7 @@
u8 buf[100], *pos;
struct ieee80211_mgmt *mgmt;
u8 dialog_token = 1;
+ const u8 *own_addr = wnm_ap_get_own_addr(hapd, sta);
if (auto_report > 3 || timeout > 63)
return -1;
@@ -1024,8 +1072,8 @@
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
- os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.coloc_intf_req.action =
WNM_COLLOCATED_INTERFERENCE_REQ;
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index a662201..3002d91 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -194,6 +194,9 @@
if (!wpa_auth->cb->get_seqnum)
return -1;
+#ifdef CONFIG_TESTING_OPTIONS
+ os_memset(seq, 0, WPA_KEY_RSC_LEN);
+#endif /* CONFIG_TESTING_OPTIONS */
res = wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
#ifdef CONFIG_TESTING_OPTIONS
if (!addr && idx < 4 && wpa_auth->conf.gtk_rsc_override_set) {
@@ -599,6 +602,15 @@
}
#endif /* CONFIG_P2P */
+ if (conf->tx_bss_auth && conf->beacon_prot) {
+ conf->tx_bss_auth->non_tx_beacon_prot = true;
+ if (!conf->tx_bss_auth->conf.beacon_prot)
+ conf->tx_bss_auth->conf.beacon_prot = true;
+ if (!conf->tx_bss_auth->conf.group_mgmt_cipher)
+ conf->tx_bss_auth->conf.group_mgmt_cipher =
+ conf->group_mgmt_cipher;
+ }
+
return wpa_auth;
}
@@ -618,6 +630,17 @@
}
+static void wpa_auth_free_conf(struct wpa_auth_config *conf)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ wpabuf_free(conf->eapol_m1_elements);
+ conf->eapol_m1_elements = NULL;
+ wpabuf_free(conf->eapol_m3_elements);
+ conf->eapol_m3_elements = NULL;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
/**
* wpa_deinit - Deinitialize WPA authenticator
* @wpa_auth: Pointer to WPA authenticator data from wpa_init()
@@ -651,6 +674,7 @@
bin_clear_free(prev, sizeof(*prev));
}
+ wpa_auth_free_conf(&wpa_auth->conf);
os_free(wpa_auth);
}
@@ -668,6 +692,7 @@
if (!wpa_auth)
return 0;
+ wpa_auth_free_conf(&wpa_auth->conf);
os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
if (wpa_auth_gen_wpa_ie(wpa_auth)) {
wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
@@ -900,19 +925,70 @@
struct wpa_state_machine *sm,
struct wpa_eapol_ie_parse *kde)
{
- struct wpa_ie_data ie;
+ struct wpa_ie_data ie, assoc_ie;
struct rsn_mdie *mdie;
+ unsigned int i, j;
+ bool found = false;
+
+ /* Verify that PMKR1Name from EAPOL-Key message 2/4 matches the value
+ * we derived. */
if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 ||
- ie.num_pmkid != 1 || !ie.pmkid) {
+ ie.num_pmkid < 1 || !ie.pmkid) {
wpa_printf(MSG_DEBUG,
"FT: No PMKR1Name in FT 4-way handshake message 2/4");
return -1;
}
- os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant",
- sm->sup_pmk_r1_name, PMKID_LEN);
+ if (wpa_parse_wpa_ie_rsn(sm->wpa_ie, sm->wpa_ie_len, &assoc_ie) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Could not parse (Re)Association Request frame RSNE");
+ os_memset(&assoc_ie, 0, sizeof(assoc_ie));
+ /* Continue to allow PMKR1Name matching to be done to cover the
+ * case where it is the only listed PMKID. */
+ }
+
+ for (i = 0; i < ie.num_pmkid; i++) {
+ const u8 *pmkid = ie.pmkid + i * PMKID_LEN;
+
+ if (os_memcmp_const(pmkid, sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RSNE[PMKID[%u]] from supplicant matches PMKR1Name",
+ i);
+ found = true;
+ } else {
+ for (j = 0; j < assoc_ie.num_pmkid; j++) {
+ if (os_memcmp(pmkid,
+ assoc_ie.pmkid + j * PMKID_LEN,
+ PMKID_LEN) == 0)
+ break;
+ }
+
+ if (j == assoc_ie.num_pmkid) {
+ wpa_printf(MSG_DEBUG,
+ "FT: RSNE[PMKID[%u]] from supplicant is neither PMKR1Name nor included in AssocReq",
+ i);
+ found = false;
+ break;
+ }
+ wpa_printf(MSG_DEBUG,
+ "FT: RSNE[PMKID[%u]] from supplicant is not PMKR1Name, but matches a PMKID in AssocReq",
+ i);
+ }
+ }
+
+ if (!found) {
+ wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "PMKR1Name mismatch in FT 4-way handshake");
+ wpa_hexdump(MSG_DEBUG,
+ "FT: PMKIDs/PMKR1Name from Supplicant",
+ ie.pmkid, ie.num_pmkid * PMKID_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
+ sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+ return -1;
+ }
if (!kde->mdie || !kde->ftie) {
wpa_printf(MSG_DEBUG,
@@ -1076,28 +1152,166 @@
}
+enum eapol_key_msg { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST };
+
+static bool wpa_auth_valid_key_desc_ver(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm, u16 ver)
+{
+ if (ver > WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_printf(MSG_INFO, "RSN: " MACSTR
+ " used undefined Key Descriptor Version %d",
+ MAC2STR(wpa_auth_get_spa(sm)), ver);
+ return false;
+ }
+
+ if (!wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+ wpa_use_cmac(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "advertised support for AES-128-CMAC, but did not use it");
+ return false;
+ }
+
+ if (sm->pairwise != WPA_CIPHER_TKIP &&
+ !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+ !wpa_use_cmac(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "did not use HMAC-SHA1-AES with CCMP/GCMP");
+ return false;
+ }
+
+ if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool wpa_auth_valid_request_counter(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *replay_counter)
+{
+
+ if (sm->req_replay_counter_used &&
+ os_memcmp(replay_counter, sm->req_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) <= 0) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_WARNING,
+ "received EAPOL-Key request with replayed counter");
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool wpa_auth_valid_counter(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const struct wpa_eapol_key *key,
+ enum eapol_key_msg msg,
+ const char *msgtxt)
+{
+ int i;
+
+ if (msg == REQUEST)
+ return wpa_auth_valid_request_counter(wpa_auth, sm,
+ key->replay_counter);
+
+ if (wpa_replay_counter_valid(sm->key_replay, key->replay_counter))
+ return true;
+
+ if (msg == PAIRWISE_2 &&
+ wpa_replay_counter_valid(sm->prev_key_replay,
+ key->replay_counter) &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+ os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+ /*
+ * Some supplicant implementations (e.g., Windows XP
+ * WZC) update SNonce for each EAPOL-Key 2/4. This
+ * breaks the workaround on accepting any of the
+ * pending requests, so allow the SNonce to be updated
+ * even if we have already sent out EAPOL-Key 3/4.
+ */
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "Process SNonce update from STA based on retransmitted EAPOL-Key 1/4");
+ sm->update_snonce = 1;
+ os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN);
+ sm->alt_snonce_valid = true;
+ os_memcpy(sm->alt_replay_counter,
+ sm->key_replay[0].counter,
+ WPA_REPLAY_COUNTER_LEN);
+ return true;
+ }
+
+ if (msg == PAIRWISE_4 && sm->alt_snonce_valid &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+ os_memcmp(key->replay_counter, sm->alt_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) == 0) {
+ /*
+ * Supplicant may still be using the old SNonce since
+ * there was two EAPOL-Key 2/4 messages and they had
+ * different SNonce values.
+ */
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4");
+ return true;
+ }
+
+ if (msg == PAIRWISE_2 &&
+ wpa_replay_counter_valid(sm->prev_key_replay,
+ key->replay_counter) &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "ignore retransmitted EAPOL-Key %s - SNonce did not change",
+ msgtxt);
+ } else {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_DEBUG,
+ "received EAPOL-Key %s with unexpected replay counter",
+ msgtxt);
+ }
+ for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+ if (!sm->key_replay[i].valid)
+ break;
+ wpa_hexdump(MSG_DEBUG, "pending replay counter",
+ sm->key_replay[i].counter,
+ WPA_REPLAY_COUNTER_LEN);
+ }
+ wpa_hexdump(MSG_DEBUG, "received replay counter",
+ key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+ return false;
+}
+
+
void wpa_receive(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
u8 *data, size_t data_len)
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
- u16 key_info, key_data_length;
- enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg;
- char *msgtxt;
- struct wpa_eapol_ie_parse kde;
+ u16 key_info, ver, key_data_length;
+ enum eapol_key_msg msg;
+ const char *msgtxt;
const u8 *key_data;
size_t keyhdrlen, mic_len;
u8 *mic;
- bool is_mld = false;
+ u8 *key_data_buf = NULL;
+ size_t key_data_buf_len = 0;
if (!wpa_auth || !wpa_auth->conf.wpa || !sm)
return;
-#ifdef CONFIG_IEEE80211BE
- is_mld = sm->mld_assoc_link_id >= 0;
-#endif /* CONFIG_IEEE80211BE */
-
wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len);
mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
@@ -1167,11 +1381,31 @@
return;
}
- /* TODO: Make this more robust for distinguising EAPOL-Key msg 2/4 from
- * 4/4. Secure=1 is used in msg 2/4 when doing PTK rekeying, so the
- * MLD mechanism here does not work without the somewhat undesired check
- * on wpa_ptk_state.. Would likely need to decrypt Key Data first to be
- * able to know which message this is in MLO cases.. */
+ ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ if (!wpa_auth_valid_key_desc_ver(wpa_auth, sm, ver))
+ goto out;
+ if (mic_len > 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
+ sm->PTK_valid &&
+ (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+ ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
+ wpa_use_aes_key_wrap(sm->wpa_key_mgmt)) &&
+ key_data_length >= 8 && key_data_length % 8 == 0) {
+ key_data_length -= 8; /* AES-WRAP adds 8 bytes */
+ key_data_buf = os_malloc(key_data_length);
+ if (!key_data_buf)
+ goto out;
+ key_data_buf_len = key_data_length;
+ if (aes_unwrap(sm->PTK.kek, sm->PTK.kek_len,
+ key_data_length / 8, key_data, key_data_buf)) {
+ wpa_printf(MSG_INFO,
+ "RSN: AES unwrap failed - could not decrypt EAPOL-Key key data");
+ goto out;
+ }
+ key_data = key_data_buf;
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Decrypted EAPOL-Key Key Data",
+ key_data, key_data_length);
+ }
+
if (key_info & WPA_KEY_INFO_REQUEST) {
msg = REQUEST;
msgtxt = "Request";
@@ -1179,10 +1413,13 @@
msg = GROUP_2;
msgtxt = "2/2 Group";
} else if (key_data_length == 0 ||
+ (sm->wpa == WPA_VERSION_WPA2 &&
+ (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA) ||
+ key_data_buf) &&
+ (key_info & WPA_KEY_INFO_SECURE) &&
+ !get_ie(key_data, key_data_length, WLAN_EID_RSN)) ||
(mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
- key_data_length == AES_BLOCK_SIZE) ||
- (is_mld && (key_info & WPA_KEY_INFO_SECURE) &&
- sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING)) {
+ key_data_length == AES_BLOCK_SIZE)) {
msg = PAIRWISE_4;
msgtxt = "4/4 Pairwise";
} else {
@@ -1190,127 +1427,15 @@
msgtxt = "2/4 Pairwise";
}
- if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
- msg == GROUP_2) {
- u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
- if (sm->pairwise == WPA_CIPHER_CCMP ||
- sm->pairwise == WPA_CIPHER_GCMP) {
- if (wpa_use_cmac(sm->wpa_key_mgmt) &&
- !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
- ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
- wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_WARNING,
- "advertised support for AES-128-CMAC, but did not use it");
- return;
- }
+ if (!wpa_auth_valid_counter(wpa_auth, sm, key, msg, msgtxt))
+ goto out;
- if (!wpa_use_cmac(sm->wpa_key_mgmt) &&
- !wpa_use_akm_defined(sm->wpa_key_mgmt) &&
- ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
- wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_WARNING,
- "did not use HMAC-SHA1-AES with CCMP/GCMP");
- return;
- }
- }
-
- if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
- ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
- wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_WARNING,
- "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
- return;
- }
- }
-
- if (key_info & WPA_KEY_INFO_REQUEST) {
- if (sm->req_replay_counter_used &&
- os_memcmp(key->replay_counter, sm->req_replay_counter,
- WPA_REPLAY_COUNTER_LEN) <= 0) {
- wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_WARNING,
- "received EAPOL-Key request with replayed counter");
- return;
- }
- }
-
- if (!(key_info & WPA_KEY_INFO_REQUEST) &&
- !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) {
- int i;
-
- if (msg == PAIRWISE_2 &&
- wpa_replay_counter_valid(sm->prev_key_replay,
- key->replay_counter) &&
- sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
- os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0)
- {
- /*
- * Some supplicant implementations (e.g., Windows XP
- * WZC) update SNonce for each EAPOL-Key 2/4. This
- * breaks the workaround on accepting any of the
- * pending requests, so allow the SNonce to be updated
- * even if we have already sent out EAPOL-Key 3/4.
- */
- wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_DEBUG,
- "Process SNonce update from STA based on retransmitted EAPOL-Key 1/4");
- sm->update_snonce = 1;
- os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN);
- sm->alt_snonce_valid = true;
- os_memcpy(sm->alt_replay_counter,
- sm->key_replay[0].counter,
- WPA_REPLAY_COUNTER_LEN);
- goto continue_processing;
- }
-
- if (msg == PAIRWISE_4 && sm->alt_snonce_valid &&
- sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
- os_memcmp(key->replay_counter, sm->alt_replay_counter,
- WPA_REPLAY_COUNTER_LEN) == 0) {
- /*
- * Supplicant may still be using the old SNonce since
- * there was two EAPOL-Key 2/4 messages and they had
- * different SNonce values.
- */
- wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_DEBUG,
- "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4");
- goto continue_processing;
- }
-
- if (msg == PAIRWISE_2 &&
- wpa_replay_counter_valid(sm->prev_key_replay,
- key->replay_counter) &&
- sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
- wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_DEBUG,
- "ignore retransmitted EAPOL-Key %s - SNonce did not change",
- msgtxt);
- } else {
- wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_DEBUG,
- "received EAPOL-Key %s with unexpected replay counter",
- msgtxt);
- }
- for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
- if (!sm->key_replay[i].valid)
- break;
- wpa_hexdump(MSG_DEBUG, "pending replay counter",
- sm->key_replay[i].counter,
- WPA_REPLAY_COUNTER_LEN);
- }
- wpa_hexdump(MSG_DEBUG, "received replay counter",
- key->replay_counter, WPA_REPLAY_COUNTER_LEN);
- return;
- }
-
-continue_processing:
#ifdef CONFIG_FILS
if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 &&
!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
"WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame");
- return;
+ goto out;
}
#endif /* CONFIG_FILS */
@@ -1324,7 +1449,7 @@
LOGGER_INFO,
"received EAPOL-Key msg 2/4 in invalid state (%d) - dropped",
sm->wpa_ptk_state);
- return;
+ goto out;
}
random_add_randomness(key->key_nonce, WPA_NONCE_LEN);
if (sm->group->reject_4way_hs_for_entropy) {
@@ -1342,7 +1467,7 @@
random_mark_pool_ready();
wpa_sta_disconnect(wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
- return;
+ goto out;
}
break;
case PAIRWISE_4:
@@ -1352,7 +1477,7 @@
LOGGER_INFO,
"received EAPOL-Key msg 4/4 in invalid state (%d) - dropped",
sm->wpa_ptk_state);
- return;
+ goto out;
}
break;
case GROUP_2:
@@ -1362,10 +1487,20 @@
LOGGER_INFO,
"received EAPOL-Key msg 2/2 in invalid state (%d) - dropped",
sm->wpa_ptk_group_state);
- return;
+ goto out;
}
break;
case REQUEST:
+ if (sm->wpa_ptk_state == WPA_PTK_PTKSTART ||
+ sm->wpa_ptk_state == WPA_PTK_PTKCALCNEGOTIATING ||
+ sm->wpa_ptk_state == WPA_PTK_PTKCALCNEGOTIATING2 ||
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
+ wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key Request in invalid state (%d) - dropped",
+ sm->wpa_ptk_state);
+ goto out;
+ }
break;
}
@@ -1375,14 +1510,14 @@
if (key_info & WPA_KEY_INFO_ACK) {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
"received invalid EAPOL-Key: Key Ack set");
- return;
+ goto out;
}
if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
!(key_info & WPA_KEY_INFO_MIC)) {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
"received invalid EAPOL-Key: Key MIC not set");
- return;
+ goto out;
}
#ifdef CONFIG_FILS
@@ -1390,7 +1525,7 @@
(key_info & WPA_KEY_INFO_MIC)) {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
"received invalid EAPOL-Key: Key MIC set");
- return;
+ goto out;
}
#endif /* CONFIG_FILS */
@@ -1409,7 +1544,7 @@
"TEST: Ignore Key MIC failure for fuzz testing");
goto continue_fuzz;
#endif /* TEST_FUZZ */
- return;
+ goto out;
}
#ifdef CONFIG_FILS
if (!mic_len &&
@@ -1423,7 +1558,7 @@
"TEST: Ignore Key MIC failure for fuzz testing");
goto continue_fuzz;
#endif /* TEST_FUZZ */
- return;
+ goto out;
}
#endif /* CONFIG_FILS */
#ifdef TEST_FUZZ
@@ -1435,6 +1570,12 @@
}
if (key_info & WPA_KEY_INFO_REQUEST) {
+ if (!(key_info & WPA_KEY_INFO_SECURE)) {
+ wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
+ LOGGER_INFO,
+ "received EAPOL-Key request without Secure=1");
+ goto out;
+ }
if (sm->MICVerified) {
sm->req_replay_counter_used = 1;
os_memcpy(sm->req_replay_counter, key->replay_counter,
@@ -1443,28 +1584,19 @@
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
LOGGER_INFO,
"received EAPOL-Key request with invalid MIC");
- return;
+ goto out;
}
- /*
- * TODO: should decrypt key data field if encryption was used;
- * even though MAC address KDE is not normally encrypted,
- * supplicant is allowed to encrypt it.
- */
if (key_info & WPA_KEY_INFO_ERROR) {
if (wpa_receive_error_report(
wpa_auth, sm,
!(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
- return; /* STA entry was removed */
+ goto out; /* STA entry was removed */
} else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
LOGGER_INFO,
"received EAPOL-Key Request for new 4-Way Handshake");
wpa_request_new_ptk(sm);
- } else if (key_data_length > 0 &&
- wpa_parse_kde_ies(key_data, key_data_length,
- &kde) == 0 &&
- kde.mac_addr) {
} else {
wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm),
LOGGER_INFO,
@@ -1506,7 +1638,7 @@
os_free(sm->last_rx_eapol_key);
sm->last_rx_eapol_key = os_memdup(data, data_len);
if (!sm->last_rx_eapol_key)
- return;
+ goto out;
sm->last_rx_eapol_key_len = data_len;
sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
@@ -1515,6 +1647,9 @@
sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST);
os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN);
wpa_sm_step(sm);
+
+out:
+ bin_clear_free(key_data_buf, key_data_buf_len);
}
@@ -2332,10 +2467,14 @@
SM_STATE(WPA_PTK, PTKSTART)
{
- u8 buf[2 * (2 + RSN_SELECTOR_LEN) + PMKID_LEN + ETH_ALEN];
+ u8 *buf;
+ size_t buf_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
u8 *pmkid = NULL;
size_t kde_len = 0;
u16 key_info;
+#ifdef CONFIG_TESTING_OPTIONS
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+#endif /* CONFIG_TESTING_OPTIONS */
SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
sm->PTKRequest = false;
@@ -2350,6 +2489,19 @@
return;
}
+#ifdef CONFIG_IEEE80211BE
+ if (sm->mld_assoc_link_id >= 0)
+ buf_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->eapol_m1_elements)
+ buf_len += wpabuf_len(conf->eapol_m1_elements);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ buf = os_zalloc(buf_len);
+ if (!buf)
+ return;
+
wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm), LOGGER_DEBUG,
"sending 1/4 msg of 4-Way Handshake");
/*
@@ -2453,11 +2605,20 @@
}
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->eapol_m1_elements) {
+ os_memcpy(buf + kde_len, wpabuf_head(conf->eapol_m1_elements),
+ wpabuf_len(conf->eapol_m1_elements));
+ kde_len += wpabuf_len(conf->eapol_m1_elements);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
key_info = WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE;
if (sm->pairwise_set && sm->wpa != WPA_VERSION_WPA)
key_info |= WPA_KEY_INFO_SECURE;
wpa_send_eapol(sm->wpa_auth, sm, key_info, NULL,
sm->ANonce, kde_len ? buf : NULL, kde_len, 0, 0);
+ os_free(buf);
}
@@ -3178,7 +3339,7 @@
/* MLD MAC address must be the same */
if (!kde->mac_addr ||
- os_memcmp(kde->mac_addr, sm->peer_mld_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(kde->mac_addr, sm->peer_mld_addr)) {
wpa_printf(MSG_DEBUG, "RSN: MLD: Invalid MLD address");
return -1;
}
@@ -3205,8 +3366,8 @@
return -1;
}
- if (os_memcmp(sm->mld_links[i].peer_addr, kde->mlo_link[i] + 1,
- ETH_ALEN) != 0) {
+ if (!ether_addr_equal(sm->mld_links[i].peer_addr,
+ kde->mlo_link[i] + 1)) {
wpa_printf(MSG_DEBUG,
"RSN: MLD: invalid MAC address=" MACSTR
" expected " MACSTR " (link ID %u)",
@@ -3240,7 +3401,7 @@
size_t pmk_len;
int ft;
const u8 *eapol_key_ie, *key_data, *mic;
- u16 key_data_length;
+ u16 key_info, ver, key_data_length;
size_t mic_len, eapol_key_ie_len;
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
@@ -3250,6 +3411,8 @@
u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
u8 pmk_r1[PMK_LEN_MAX];
size_t key_len;
+ u8 *key_data_buf = NULL;
+ size_t key_data_buf_len = 0;
SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
sm->EAPOLKeyReceived = false;
@@ -3357,12 +3520,46 @@
hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
key = (struct wpa_eapol_key *) (hdr + 1);
mic = (u8 *) (key + 1);
+ key_info = WPA_GET_BE16(key->key_info);
key_data = mic + mic_len + 2;
key_data_length = WPA_GET_BE16(mic + mic_len);
if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
sizeof(*key) - mic_len - 2)
goto out;
+ ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ if (mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ if (ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
+ ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+ !wpa_use_aes_key_wrap(sm->wpa_key_mgmt)) {
+ wpa_printf(MSG_INFO,
+ "Unsupported EAPOL-Key Key Data field encryption");
+ goto out;
+ }
+
+ if (key_data_length < 8 || key_data_length % 8) {
+ wpa_printf(MSG_INFO,
+ "RSN: Unsupported AES-WRAP len %u",
+ key_data_length);
+ goto out;
+ }
+ key_data_length -= 8; /* AES-WRAP adds 8 bytes */
+ key_data_buf = os_malloc(key_data_length);
+ if (!key_data_buf)
+ goto out;
+ key_data_buf_len = key_data_length;
+ if (aes_unwrap(PTK.kek, PTK.kek_len, key_data_length / 8,
+ key_data, key_data_buf)) {
+ bin_clear_free(key_data_buf, key_data_buf_len);
+ wpa_printf(MSG_INFO,
+ "RSN: AES unwrap failed - could not decrypt EAPOL-Key key data");
+ goto out;
+ }
+ key_data = key_data_buf;
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Decrypted EAPOL-Key Key Data",
+ key_data, key_data_length);
+ }
+
if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
wpa_auth_vlogger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO,
"received EAPOL-Key msg 2/4 with invalid Key Data contents");
@@ -3507,27 +3704,6 @@
return;
}
-#ifdef CONFIG_IEEE80211R_AP
- if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
- /*
- * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
- * with the value we derived.
- */
- if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name,
- WPA_PMK_NAME_LEN) != 0) {
- wpa_auth_logger(sm->wpa_auth, wpa_auth_get_spa(sm),
- LOGGER_DEBUG,
- "PMKR1Name mismatch in FT 4-way handshake");
- wpa_hexdump(MSG_DEBUG,
- "FT: PMKR1Name from Supplicant",
- sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
- wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
- sm->pmk_r1_name, WPA_PMK_NAME_LEN);
- goto out;
- }
- }
-#endif /* CONFIG_IEEE80211R_AP */
-
if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) {
wpa_sta_disconnect(wpa_auth, sm->addr,
@@ -3562,6 +3738,7 @@
out:
forced_memzero(pmk_r0, sizeof(pmk_r0));
forced_memzero(pmk_r1, sizeof(pmk_r1));
+ bin_clear_free(key_data_buf, key_data_buf_len);
}
@@ -3575,14 +3752,18 @@
static int ieee80211w_kde_len(struct wpa_state_machine *sm)
{
size_t len = 0;
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
if (sm->mgmt_frame_prot) {
len += 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN;
- len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ 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;
if (sm->mgmt_frame_prot && sm->wpa_auth->conf.beacon_prot) {
len += 2 + RSN_SELECTOR_LEN + WPA_BIGTK_KDE_PREFIX_LEN;
- len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
}
return len;
@@ -3595,7 +3776,8 @@
struct wpa_bigtk_kde bigtk;
struct wpa_group *gsm = sm->group;
u8 rsc[WPA_KEY_RSC_LEN];
- struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_auth_config *conf = &wpa_auth->conf;
size_t len = wpa_cipher_key_len(conf->group_mgmt_cipher);
if (!sm->mgmt_frame_prot)
@@ -3627,7 +3809,14 @@
NULL, 0);
forced_memzero(&igtk, sizeof(igtk));
- if (!conf->beacon_prot)
+ if (wpa_auth->conf.tx_bss_auth) {
+ wpa_auth = wpa_auth->conf.tx_bss_auth;
+ conf = &wpa_auth->conf;
+ len = wpa_cipher_key_len(conf->group_mgmt_cipher);
+ gsm = wpa_auth->group;
+ }
+
+ if (!sm->wpa_auth->conf.beacon_prot)
return pos;
bigtk.keyid[0] = gsm->GN_bigtk;
@@ -3785,6 +3974,11 @@
if (!beacon_prot)
return;
+ if (a->conf.tx_bss_auth) {
+ a = a->conf.tx_bss_auth;
+ gsm = a->group;
+ }
+
info->bigtkidx = gsm->GN_bigtk;
info->bigtk = gsm->BIGTK[gsm->GN_bigtk - 6];
@@ -3807,6 +4001,7 @@
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;
@@ -3825,10 +4020,15 @@
return kde_len;
/* MLO IGTK KDE for each link */
- igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ 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->wpa_auth->conf.beacon_prot)
+ 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.beacon_prot)
return kde_len;
/* MLO BIGTK KDE for each link */
@@ -3865,7 +4065,8 @@
/* Add MLO GTK KDEs */
for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- if (!sm->mld_links[link_id].valid)
+ if (!sm->mld_links[link_id].valid ||
+ !ml_key_info.links[i].gtk_len)
continue;
wpa_printf(MSG_DEBUG, "RSN: MLO GTK: link=%u", link_id);
@@ -3897,7 +4098,8 @@
/* Add MLO IGTK KDEs */
for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- if (!sm->mld_links[link_id].valid)
+ if (!sm->mld_links[link_id].valid ||
+ !ml_key_info.links[i].igtk_len)
continue;
wpa_printf(MSG_DEBUG, "RSN: MLO IGTK: link=%u", link_id);
@@ -3936,7 +4138,9 @@
/* Add MLO BIGTK KDEs */
for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) {
- if (!sm->mld_links[link_id].valid)
+ if (!sm->mld_links[link_id].valid ||
+ !ml_key_info.links[i].bigtk ||
+ !ml_key_info.links[i].igtk_len)
continue;
wpa_printf(MSG_DEBUG, "RSN: MLO BIGTK: link=%u", link_id);
@@ -4233,6 +4437,11 @@
kde_len += wpa_auth_ml_kdes_len(sm);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->eapol_m3_elements)
+ kde_len += wpabuf_len(conf->eapol_m3_elements);
+#endif /* CONFIG_TESTING_OPTIONS */
+
kde = os_malloc(kde_len);
if (!kde)
goto done;
@@ -4248,7 +4457,7 @@
size_t elen;
elen = pos - kde;
- res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
+ res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name, true);
if (res < 0) {
wpa_printf(MSG_ERROR,
"FT: Failed to insert PMKR1Name into RSN IE in EAPOL-Key data");
@@ -4347,6 +4556,17 @@
pos = wpa_auth_ml_kdes(sm, pos);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (conf->eapol_m3_elements) {
+ os_memcpy(pos, wpabuf_head(conf->eapol_m3_elements),
+ wpabuf_len(conf->eapol_m3_elements));
+ pos += wpabuf_len(conf->eapol_m3_elements);
+ }
+
+ if (conf->eapol_m3_no_encrypt)
+ encr = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
wpa_send_eapol(sm->wpa_auth, sm,
(secure ? WPA_KEY_INFO_SECURE : 0) |
(wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
@@ -4398,7 +4618,7 @@
/* MLD MAC address must be the same */
if (!kde.mac_addr ||
- os_memcmp(kde.mac_addr, sm->peer_mld_addr, ETH_ALEN) != 0) {
+ !ether_addr_equal(kde.mac_addr, sm->peer_mld_addr)) {
wpa_printf(MSG_DEBUG,
"MLD: Mismatching or missing MLD address in EAPOL-Key msg 4/4");
return -1;
@@ -4732,7 +4952,8 @@
return;
kde = pos = kde_buf;
- wpa_auth_ml_group_kdes(sm, pos);
+ pos = wpa_auth_ml_group_kdes(sm, pos);
+ kde_len = pos - kde_buf;
}
#endif /* CONFIG_IEEE80211BE */
} else {
@@ -4912,19 +5133,30 @@
group->IGTK[group->GN_igtk - 4], len);
}
- if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
- conf->beacon_prot) {
- len = wpa_cipher_key_len(conf->group_mgmt_cipher);
- os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
- inc_byte_array(group->Counter, WPA_NONCE_LEN);
- if (wpa_gmk_to_gtk(group->GMK, "BIGTK key expansion",
- wpa_auth->addr, group->GNonce,
- group->BIGTK[group->GN_bigtk - 6], len) < 0)
- ret = -1;
- wpa_hexdump_key(MSG_DEBUG, "BIGTK",
- group->BIGTK[group->GN_bigtk - 6], len);
+ if (!wpa_auth->non_tx_beacon_prot &&
+ conf->ieee80211w == NO_MGMT_FRAME_PROTECTION)
+ return ret;
+ if (!conf->beacon_prot)
+ return ret;
+
+ if (wpa_auth->conf.tx_bss_auth) {
+ group = wpa_auth->conf.tx_bss_auth->group;
+ if (group->bigtk_set)
+ return ret;
+ wpa_printf(MSG_DEBUG, "Set up BIGTK for TX BSS");
}
+ len = wpa_cipher_key_len(conf->group_mgmt_cipher);
+ os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+ inc_byte_array(group->Counter, WPA_NONCE_LEN);
+ if (wpa_gmk_to_gtk(group->GMK, "BIGTK key expansion",
+ wpa_auth->addr, group->GNonce,
+ group->BIGTK[group->GN_bigtk - 6], len) < 0)
+ return -1;
+ group->bigtk_set = true;
+ wpa_hexdump_key(MSG_DEBUG, "BIGTK",
+ group->BIGTK[group->GN_bigtk - 6], len);
+
return ret;
}
@@ -5085,9 +5317,10 @@
int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
{
- struct wpa_group *gsm = sm->group;
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_group *gsm = wpa_auth->group;
u8 *start = pos;
- size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ size_t len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
/*
* BIGTK subelement:
@@ -5097,7 +5330,7 @@
*pos++ = 2 + 6 + len;
WPA_PUT_LE16(pos, gsm->GN_bigtk);
pos += 2;
- if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos) != 0)
+ if (wpa_auth_get_seqnum(wpa_auth, NULL, gsm->GN_bigtk, pos) != 0)
return 0;
pos += 6;
@@ -5187,12 +5420,21 @@
KEY_FLAG_GROUP_TX_DEFAULT) < 0)
ret = -1;
- if (ret == 0 && conf->beacon_prot &&
- wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+ if (ret || !conf->beacon_prot)
+ return ret;
+ if (wpa_auth->conf.tx_bss_auth) {
+ wpa_auth = wpa_auth->conf.tx_bss_auth;
+ group = wpa_auth->group;
+ if (!group->bigtk_set || group->bigtk_configured)
+ return ret;
+ }
+ if (wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
broadcast_ether_addr, group->GN_bigtk,
group->BIGTK[group->GN_bigtk - 6], len,
KEY_FLAG_GROUP_TX_DEFAULT) < 0)
ret = -1;
+ else
+ group->bigtk_configured = true;
}
return ret;
@@ -5337,9 +5579,11 @@
tmp = group->GM_igtk;
group->GM_igtk = group->GN_igtk;
group->GN_igtk = tmp;
- tmp = group->GM_bigtk;
- group->GM_bigtk = group->GN_bigtk;
- group->GN_bigtk = tmp;
+ if (!wpa_auth->conf.tx_bss_auth) {
+ tmp = group->GM_bigtk;
+ group->GM_bigtk = group->GN_bigtk;
+ group->GN_bigtk = tmp;
+ }
wpa_gtk_update(wpa_auth, group);
wpa_group_config_group_keys(wpa_auth, group);
}
@@ -5689,28 +5933,11 @@
int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
- int session_timeout, int akmp)
-{
- if (!wpa_auth || wpa_auth->conf.disable_pmksa_caching)
- return -1;
-
- wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (2)", pmk, PMK_LEN);
- if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
- NULL, 0, wpa_auth->addr, addr, session_timeout,
- NULL, akmp))
- return 0;
-
- return -1;
-}
-
-
-int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
- const u8 *pmk, size_t pmk_len, const u8 *pmkid,
int session_timeout, int akmp, const u8 *dpp_pkhash)
{
struct rsn_pmksa_cache_entry *entry;
- if (wpa_auth->conf.disable_pmksa_caching)
+ if (!wpa_auth || wpa_auth->conf.disable_pmksa_caching)
return -1;
wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (3)", pmk, PMK_LEN);
@@ -5834,13 +6061,14 @@
void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
struct wpa_state_machine *sm,
struct wpa_authenticator *wpa_auth,
- u8 *pmkid, u8 *pmk)
+ u8 *pmkid, u8 *pmk, size_t *pmk_len)
{
if (!sm)
return;
sm->pmksa = pmksa;
- os_memcpy(pmk, pmksa->pmk, PMK_LEN);
+ os_memcpy(pmk, pmksa->pmk, pmksa->pmk_len);
+ *pmk_len = pmksa->pmk_len;
os_memcpy(pmkid, pmksa->pmkid, PMKID_LEN);
os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmksa->pmkid, PMKID_LEN);
}
@@ -6358,7 +6586,7 @@
size_t elen;
elen = pos - kde;
- res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
+ res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name, true);
if (res < 0) {
wpa_printf(MSG_ERROR,
"FT: Failed to insert PMKR1Name into RSN IE in EAPOL-Key data");
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 57fda8a..4f6bb76 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -244,6 +244,9 @@
unsigned int skip_send_eapol:1;
unsigned int enable_eapol_large_timeout:1;
bool delay_eapol_tx;
+ struct wpabuf *eapol_m1_elements;
+ struct wpabuf *eapol_m3_elements;
+ bool eapol_m3_no_encrypt;
#endif /* CONFIG_TESTING_OPTIONS */
unsigned int oci_freq_override_eapol_m3;
unsigned int oci_freq_override_eapol_g1;
@@ -279,6 +282,11 @@
bool force_kdk_derivation;
bool radius_psk;
+
+ /* 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;
};
typedef enum {
@@ -479,9 +487,6 @@
void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
- int session_timeout, int akmp);
-int wpa_auth_pmksa_add3(struct wpa_authenticator *wpa_auth, const u8 *addr,
- const u8 *pmk, size_t pmk_len, const u8 *pmkid,
int session_timeout, int akmp, const u8 *dpp_pkhash);
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr);
@@ -507,7 +512,7 @@
void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
struct wpa_state_machine *sm,
struct wpa_authenticator *wpa_auth,
- u8 *pmkid, u8 *pmk);
+ u8 *pmkid, u8 *pmk, size_t *pmk_len);
int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int ack);
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 4b16f62..7744ed6 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -1427,7 +1427,7 @@
os_get_reltime(&now);
dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
- if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(r0->spa, spa) &&
os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
WPA_PMK_NAME_LEN) == 0) {
*r0_out = r0;
@@ -1522,7 +1522,7 @@
os_get_reltime(&now);
dl_list_for_each(r1, &cache->pmk_r1, struct wpa_ft_pmk_r1_sa, list) {
- if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 &&
+ if (ether_addr_equal(r1->spa, spa) &&
os_memcmp_const(r1->pmk_r1_name, pmk_r1_name,
WPA_PMK_NAME_LEN) == 0) {
os_memcpy(pmk_r1, r1->pmk_r1, r1->pmk_r1_len);
@@ -2024,7 +2024,7 @@
sm->r0kh_id, sm->r0kh_id_len);
return -1;
}
- if (os_memcmp(r0kh->addr, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(r0kh->addr, sm->wpa_auth->addr)) {
wpa_printf(MSG_DEBUG,
"FT: R0KH-ID points to self - no matching key available");
return -1;
@@ -2366,7 +2366,8 @@
static u8 * wpa_ft_bigtk_subelem(struct wpa_state_machine *sm, size_t *len)
{
u8 *subelem, *pos;
- struct wpa_group *gsm = sm->group;
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ struct wpa_group *gsm = wpa_auth->group;
size_t subelem_len;
const u8 *kek, *bigtk;
size_t kek_len;
@@ -2381,7 +2382,7 @@
kek_len = sm->PTK.kek_len;
}
- bigtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ bigtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
/* Sub-elem ID[1] | Length[1] | KeyID[2] | BIPN[6] | Key Length[1] |
* Key[16+8] */
@@ -2395,7 +2396,7 @@
*pos++ = subelem_len - 2;
WPA_PUT_LE16(pos, gsm->GN_bigtk);
pos += 2;
- wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos);
+ wpa_auth_get_seqnum(wpa_auth, NULL, gsm->GN_bigtk, pos);
pos += 6;
*pos++ = bigtk_len;
bigtk = gsm->BIGTK[gsm->GN_bigtk - 6];
@@ -3765,7 +3766,7 @@
" Target AP=" MACSTR " Action=%d)",
MAC2STR(sta_addr), MAC2STR(target_ap), action);
- if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(sta_addr, sm->addr)) {
wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: "
"STA=" MACSTR " STA-Address=" MACSTR,
MAC2STR(sm->addr), MAC2STR(sta_addr));
@@ -3778,7 +3779,7 @@
* APs in the MD (if such a list were configured).
*/
if ((target_ap[0] & 0x01) ||
- os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) {
+ ether_addr_equal(target_ap, sm->wpa_auth->addr)) {
wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action "
"frame");
return -1;
@@ -4036,7 +4037,7 @@
seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len,
auth, auth_len, msgtype, no_defer);
if (!no_defer && r1kh_wildcard &&
- (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+ (!r1kh || !ether_addr_equal(r1kh->addr, src_addr))) {
/* wildcard: r1kh-id unknown or changed addr -> do a seq req */
seq_ret = FT_RRB_SEQ_DEFER;
}
@@ -4203,7 +4204,7 @@
cb ? 0 : 1);
}
if (cb && r0kh_wildcard &&
- (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+ (!r0kh || !ether_addr_equal(r0kh->addr, src_addr))) {
/* wildcard: r0kh-id unknown or changed addr -> do a seq req */
seq_ret = FT_RRB_SEQ_DEFER;
}
@@ -4357,7 +4358,7 @@
struct ft_get_sta_ctx *info = ctx;
if ((info->s1kh_id &&
- os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) ||
+ !ether_addr_equal(info->s1kh_id, sm->addr)) ||
os_memcmp(info->nonce, sm->ft_pending_pull_nonce,
FT_RRB_NONCE_LEN) != 0 ||
sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
@@ -4482,7 +4483,7 @@
wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
&r0kh, &r0kh_wildcard);
if (!r0kh_wildcard &&
- (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
+ (!r0kh || !ether_addr_equal(r0kh->addr, src_addr))) {
wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
f_r0kh_id, f_r0kh_id_len);
goto out;
@@ -4500,7 +4501,7 @@
wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh,
&r1kh_wildcard);
if (!r1kh_wildcard &&
- (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
+ (!r1kh || !ether_addr_equal(r1kh->addr, src_addr))) {
wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID",
f_r1kh_id, FT_R1KH_ID_LEN);
goto out;
@@ -4806,7 +4807,7 @@
return -1;
}
- if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) {
+ if (!ether_addr_equal(target_ap_addr, wpa_auth->addr)) {
wpa_printf(MSG_DEBUG, "FT: Target AP address in the "
"RRB Request does not match with own "
"address");
@@ -4969,7 +4970,7 @@
return;
dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
- if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) {
+ if (ether_addr_equal(r0->spa, addr)) {
r0found = r0;
break;
}
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 82d79f2..b286a77 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -183,8 +183,15 @@
wconf->oci_freq_override_ft_assoc = conf->oci_freq_override_ft_assoc;
wconf->oci_freq_override_fils_assoc =
conf->oci_freq_override_fils_assoc;
+
wconf->skip_send_eapol = iconf->skip_send_eapol;
wconf->enable_eapol_large_timeout = iconf->enable_eapol_large_timeout;
+
+ if (conf->eapol_m1_elements)
+ wconf->eapol_m1_elements = wpabuf_dup(conf->eapol_m1_elements);
+ if (conf->eapol_m3_elements)
+ wconf->eapol_m3_elements = wpabuf_dup(conf->eapol_m3_elements);
+ wconf->eapol_m3_no_encrypt = conf->eapol_m3_no_encrypt;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_P2P
os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
@@ -556,7 +563,8 @@
if (sta) {
flags = hostapd_sta_flags_to_drv(sta->flags);
#ifdef CONFIG_IEEE80211BE
- if (sta->mld_info.mld_sta && (sta->flags & WLAN_STA_AUTHORIZED))
+ if (ap_sta_is_mld(hapd, sta) &&
+ (sta->flags & WLAN_STA_AUTHORIZED))
link_id = -1;
#endif /* CONFIG_IEEE80211BE */
}
@@ -669,7 +677,7 @@
hapd = iface->bss[j];
if (hapd == idata->src_hapd ||
!hapd->wpa_auth ||
- os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0)
+ !ether_addr_equal(hapd->own_addr, idata->dst))
continue;
wpa_printf(MSG_DEBUG,
@@ -859,7 +867,7 @@
MOBILITY_DOMAIN_ID_LEN) != 0)
continue; /* no matching FT SSID/mobility domain */
if (!is_multicast_ether_addr(idata->dst_addr) &&
- os_memcmp(hapd->own_addr, idata->dst_addr, ETH_ALEN) != 0)
+ !ether_addr_equal(hapd->own_addr, idata->dst_addr))
continue; /* destination address does not match */
/* defer eth_p_oui_deliver until next eloop step as this is
@@ -1157,17 +1165,25 @@
if (!sta || !sta->wpa_sm)
return -1;
- if (vlan->notempty &&
- !hostapd_vlan_valid(hapd->conf->vlan, vlan)) {
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_INFO,
- "Invalid VLAN %d%s received from FT",
- vlan->untagged, vlan->tagged[0] ? "+" : "");
- return -1;
- }
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
+ if (vlan->notempty &&
+ !hostapd_vlan_valid(hapd->conf->vlan, vlan)) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Invalid VLAN %d%s received from FT",
+ vlan->untagged, vlan->tagged[0] ?
+ "+" : "");
+ return -1;
+ }
- if (ap_sta_set_vlan(hapd, sta, vlan) < 0)
- return -1;
+ if (ap_sta_set_vlan(hapd, sta, vlan) < 0)
+ return -1;
+
+ } else {
+ if (vlan->notempty)
+ sta->vlan_id = vlan->untagged;
+ }
/* Configure wpa_group for GTK but ignore error due to driver not
* knowing this STA. */
ap_sta_bind_vlan(hapd, sta);
@@ -1190,10 +1206,15 @@
if (!sta)
return -1;
- if (sta->vlan_desc)
+ if (sta->vlan_desc) {
*vlan = *sta->vlan_desc;
- else
+ } else if ((hapd->iface->drv_flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD) &&
+ sta->vlan_id) {
+ vlan->notempty = 1;
+ vlan->untagged = sta->vlan_id;
+ } else {
os_memset(vlan, 0, sizeof(*vlan));
+ }
return 0;
}
@@ -1396,7 +1417,7 @@
wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest));
if (!is_multicast_ether_addr(ethhdr->h_dest) &&
- os_memcmp(hapd->own_addr, ethhdr->h_dest, ETH_ALEN) != 0)
+ !ether_addr_equal(hapd->own_addr, ethhdr->h_dest))
return;
wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr),
len - sizeof(*ethhdr));
@@ -1412,7 +1433,7 @@
wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr));
if (!is_multicast_ether_addr(dst_addr) &&
- os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0)
+ !ether_addr_equal(hapd->own_addr, dst_addr))
return;
wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf,
len);
@@ -1540,7 +1561,8 @@
if (!iface->bss[0]->conf->mld_ap ||
hapd->conf->mld_id != iface->bss[0]->conf->mld_id ||
- link_id != iface->bss[0]->mld_link_id)
+ 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,
@@ -1582,7 +1604,8 @@
if (!iface->bss[0]->conf->mld_ap ||
hapd->conf->mld_id != iface->bss[0]->conf->mld_id ||
- link_id != iface->bss[0]->mld_link_id)
+ 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,
@@ -1675,9 +1698,13 @@
};
const u8 *wpa_ie;
size_t wpa_ie_len;
+ struct hostapd_data *tx_bss;
hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
_conf.msg_ctx = hapd->msg_ctx;
+ tx_bss = hostapd_mbssid_get_tx_bss(hapd);
+ if (tx_bss != hapd)
+ _conf.tx_bss_auth = tx_bss->wpa_auth;
if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
_conf.tx_status = 1;
if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 74ae5ad..9ba8304 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -134,8 +134,6 @@
* Request */
u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */
size_t r0kh_id_len;
- u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
- * message 2/4 */
u8 *assoc_resp_ftie;
void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid,
@@ -222,6 +220,8 @@
u8 BIGTK[2][WPA_IGTK_MAX_LEN];
int GN_igtk, GM_igtk;
int GN_bigtk, GM_bigtk;
+ bool bigtk_set;
+ bool bigtk_configured;
/* Number of references except those in struct wpa_group->next */
unsigned int references;
unsigned int num_setup_iface;
@@ -257,6 +257,8 @@
struct rsn_pmksa_cache *pmksa;
struct wpa_ft_pmk_cache *ft_pmk_cache;
+ bool non_tx_beacon_prot;
+
#ifdef CONFIG_P2P
struct bitfield *ip_pool;
#endif /* CONFIG_P2P */
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index aacfa33..82d4d5f 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -288,7 +288,7 @@
any_psk = wpa_psk->psk;
if (mac_addr && !dev_psk &&
- os_memcmp(mac_addr, wpa_psk->addr, ETH_ALEN) == 0) {
+ ether_addr_equal(mac_addr, wpa_psk->addr)) {
dev_psk = wpa_psk->psk;
break;
}