[wpa_supplicant] Cumulative patch from commit 27e828d72
Bug: 231272394
Test: connect/disconnect to WPA2, WPA3 networks
Test: SoftAp & p2p connection
Test: Regression test(b/231636895)
BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from Open source
27e828d72 ACS: Send EHT enabled info to driver
82066bd36 nl80211: Don't force VHT channel definition with EHT
43fe1ce35 EHT: Add [EHT] flag into AP mode STA command
696ad5c2d EHT: Indicate wifi_generation=7 in wpa_supplicant STATUS output
4994c41f2 EHT: Indicate ieee80211be configuration in hostapd STATUS output
50d883710 EHT: Fix invalid length checking for EHT Capability element
6c7b2be42 SAE: Send real status code to the driver when AP rejects external auth
2c78f11a9 Fix compilation due to forward declaration of macaddr_acl
c8e822801 OpenSSL: Fix build with old library versions that do not support TLS 1.3
c24e18e5c LibreSSL: Fix compilation issue with TLS 1.3 session ticket limit
eb5e63985 LibreSSL: Fix compilation issue with RSA-OAEP
5d56cf1c7 BoringSSL: Fix compilation error due to TLS 1.3 session tickets
a561d12d2 EAP peer status notification for server not supporting RFC 5746
566ce69a8 EAP peer: Workaround for servers that do not support safe TLS renegotiation
ccb3206b6 Fix tls_connection_set_success_data() in TLS library wrappers
decac7cd1 OpenSSL: Do not send out a TLS 1.3 session ticket if caching disabled
05406f7ae EAP-PEAP server: Fix TLS 1.3 move to Phase 2 without a new session ticket
10746875e OpenSSL: Allow no OCSP response when resuming a session with TLS 1.3
2be1bcaf7 EAP-TLS peer: Fix protected success indication check for resumed session
1c66276d9 EAP-TLS server: Send final TLS message for resumed session with TLS 1.3
81e249888 OpenSSL: Limit the number of TLS 1.3 session tickets to one
d26247c3d wpa_supplicant/README-WPS: Beautifications
a8d058c93 OpenSSL: SSLKEYLOGFILE capability to allow Wireshark TLS decoding
23f389068 wolfSSL: Fix OCSP stapling
a2971f8d8 wolfSSL: Allow TLS version 1.3 to be disabled
a40e48fbe wolfSSL: Fix TLS 1.3 session handling
0c3f68f2a wolfSSL: Check for the too-short-password error in pbkdf2_sha1()
ca2622481 Check the return of pbkdf2_sha1() for errors
013cd694d wolfSSL: Fixes for FIPS builds
9d5f8168f wolfSSL: Register a FIPS callback
8f36e6c0f wolfSSL: Implement crypto_ec_key wrappers
1f7e10177 wolfSSL: Add missing free calls for wolfSSL structs
ec1cd91e7 wolfSSL: Support both DER and PEM blobs
42871a5d2 EAP-SIM/AKA peer: IMSI privacy
21098e39f EAP-SIM/AKA server: IMSI privacy
36b11bbcf OpenSSL: RSA-OAEP-SHA-256 encryption/decryption
c3d389b72 EHT: Channel switch command support
dae7940a4 EHT: Additions to hostapd_set_freq_params()
e646b11fe EHT: Indicate EHT support in Neighbor Report element
f915d52de EHT: Provide EHT capabilities in STA addition path
a6d1b4c46 EHT: Process (Re)Association Request frame capabilities
340c0e212 EHT: Parse elements received in Management frames
d54e3d049 EHT: Add operation element in AP mode Management frames
9b7202d66 EHT: Add capabilities element in AP mode Management frames
a7ea72188 EHT: Add configuration options for beamforming capabilities
8db3881c7 EHT: Add operating channel width configuration
8dcc2139f EHT: AP mode configuration options to enable/disable the support
9f7da264b nl80211: Pass station's EHT capabilities to the driver in sta_add()
0c8a9aa5d nl80211: Parse EHT capabilities from the driver
c08b735fd EHT: Define EHT elements
1a716f86a defconfig: Document IEEE 802.11ax as a published amendment
86310c220 Set hostapd hw_mode automatically based on 6 GHz op_class
664fd83d5 nl80211: Increase the buffer length for debug printing channels
563162a5f QCA vendor attribute to allow eMLSR HW mode
1e34bc49c OpenSSL: Track SSL_SESSION ex data separately
734fa392f MBO: Check association disallowed in Beacon frames, if newer
284e3ad19 Determine whether Beacon frame information is newer in scan results
28c9f29a3 scan: Print SSID in scan results dump
5a0471579 Install wpa_passphrase when not disabled
f1686d776 hostapd: Allow enabling background radar
08d7738bb wolfSSL: Speed up crypto_ec_point_compute_y_sqr()
f50d5c9a8 wolfSSL: Fix crypto_ec_point_compute_y_sqr() error case processing
7302aa761 wolfSSL: Fix the memory leak of crypto_ec_point_compute_y_sqr()
e7dd0fff1 wolfSSL: Use wc_HmacInit() to avoid potential use of uninitialized values
f7be558d6 OpenSSL: Fix build with BoringSSL
6d33ef362 OpenSSL: Remove compatibility options for older versions than 1.0.2
78c2a4cd0 OpenSSL: Drop compatibility options for LibreSSL older than 2.7
b06250767 OpenSSL: Implement crypto_ecdh routines without EC_KEY for OpenSSL 3.0
fc96f6802 OpenSSL: Use new name for the EC_POINT set/get coordinate functions
0aae045af ctrl: Print the source address of the received commands
f94214968 wpa_ctrl: Wait for a total of 10 seconds, not 10 seconds per iteration
0d9be8855 wolfSSL: Fix certificate commonName checking
94e0f39d9 wolfSSL: Use wolfSSL_export_keying_material() when available
c31fc7a64 wolfSSL: Fix crypto_dh_init() and dh5_init()
d7b8c6eef wolfSSL: Fix crypto_ecdh_* with ECC_TIMING_RESISTANT
ae1fb6455 EAP-EKE server: Fix a memory leak on an error path
166acab4e wolfSSL: TLS session caching
12dee16d7 wolfSSL: Add a debug logging callback
a5d190650 wolfSSL: Implement tls_get_tls_unique()
a419fef36 wolfSSL: Implement tls_connection_get_cipher_suite()
364876b7d wolfSSL: Implement tls_connection_get_peer_subject()
d9c716400 wolfSSL: Implement tls_connection_get_own_cert_used()
d677b9dc6 wolfSSL: Conditional build for aes_wrap/aes_unwrap()
b0f016b87 eapol_test: Update with src/ap/ieee802_1x.c changes
747c5f228 Include MS_FUNCS=y for EAP-pwd peer build
c7f71fb86 Include HMAC-SHA384/512 KDF for SAE if SHA384/512 is included
3a759dcc8 ACS: Honor acs_exclude_dfs with hostapd's ACS implementation
3240cedd6 eapol_test: Print out names for additional known EAP types
f5c711c85 OpenSSL: Unload providers only at process exit
33c4dd26c BSS coloring: Handle the collision and CCA events coming from the kernel
27b4cc712 nl80211: Handle driver events for BSS coloring
399d6e64d nl80211: Add the switch_color() handler for BSS color changes
86bd90eb3 BSS coloring: Disable BSS color during CCA
f7d0b740e BSS coloring: BSS Color Change Announcement element generation
654d2395d BSS coloring: Handling of collision events and triggering CCA
52e2516f1 wpa_supplicant: Add the CONFIG_HE_OVERRIDES option to the defconfig
6a2a60f1d OpenSSL: Do not use the deprecated RSAPrivateKey function
ebb3055e1 OpenSSL: Generate DH parameters automatically if not set with dh_file
bcd299b32 OpenSSL: Convert DH/DSA parameter loading to new API
28c1c91d0 Remove unused dh_blob parameter
4a774cf31 Remove useless DH file configuration from TLS library wrappers
65652c67f Remove DH file configuration from TLS client functionality
b94371af8 RADIUS attributes for EAPOL-Key message details
24763e3cd RADIUS: Attributes with Extended Types (RFC 6929)
feed2f9e7 BoringSSL: Use accessor functions for X509 key usage flags
80be88a08 BoringSSL: Replace stack-allocated X509_STORE_CTX with heap one
b95ed17f6 OpenSSL: Fix build with BoringSSL and LibreSSL 3.3.x and older
ae0f6ee97 OpenSSL: CMAC using the OpenSSL library for non-FIPS cases as well
0c61f6234 OpenSSL: Implement CMAC using the EVP_MAC API
4fcd29660 OpenSSL: Extend CMAC to support 192-bit AES
117617843 OpenSSL: Remove now unused compatibility wrapper for RSA_bits()
a2dbb2558 Android: Compile hs20-osu-client to /vendor/bin in test builds
b0769ce61 DPP: Allow a list of supported curves to be used in bootstrapping URI
ef85328a6 QCA vendor command support to reset configuration for eLNA bypass
7008c50fa OpenSSL: Implement DH using the EVP API
e31500ade OpenSSL: Implement HMAC using the EVP_MAC API
097ca6bf0 OpenSSL: Unload providers on deinit
092efd45a OpenSSL: Implement AES keywrap using the EVP API
7e4984d9c OpenSSL: Use a correct EVP_CIPHER_CTX freeing function on an error path
8e0ac5366 RRM: Include passive channels in active beacon report scan
0adc67612 wpa_supplicant: Use unique IDs for networks and credentials
dacb6d278 Update IEEE P802.11ax draft references to published amendment
8128ea76a Add Transmit Power Envelope element in 6 GHz
bc3dc72a3 Extend 6 GHz Operation Info field in HE Operation element
0eb686637 hostapd: Add config option to specify 6 GHz regulatory AP type
ee06165e9 hostapd: Extend Country element to support 6 GHz band
f5ad97245 PASN: Fix build without CONFIG_TESTING_OPTIONS=y
3467a701c wpa_supplicant: Do not associate on 6 GHz with forbidden configurations
43c6eb5e4 SAE-PK: Add the option to the defconfigs
0482251a6 EAP-TLS: Allow TLSv1.3 support to be enabled with build config
7114e5606 EAP-TLS: Testing functionality to skip protected success indication
95fd54b86 Disconnect STA on continuous EAP reauth without 4-way handshake completion
9e11e746f EAP-TLS: Do not allow TLSv1.3 success without protected result indication
6135a8a6a Stop authentication attemps if AP does not disconnect us
88ab59d71 EAP-TLS: Replace the Commitment Message term with RFC 9190 language
63f311b10 EAP-TLS: Update specification references to RFC 5216 and 9190
5ab385321 Revert "Android: Compile hs20-osu-client to /vendor/bin in test builds"
b746cb28b Add support for not transmitting EAPOL-Key group msg 2/2
d27f7bd94 FILS: Fix config check to allow unsolicited broadcast Probe Response
65a3a273c OWE: Reuse own DH private key in AP if STA tries OWE association again
6ff8bda99 hostapd: Add the missing CONFIG_SAE option to the defconfig
1f5b6085c Fix SIGSEGV of eapol_test
576662d27 ieee802_11_auth: Coding style cleanup - NULL comparison
945acf3ef ieee802_11_auth: Coding style cleanup - no string constant splitting
1c3438fec RADIUS ACL/PSK check during 4-way handshake
5b5c954c0 Fix AP config check to recognize all PSK AKMs
c5d9f9064 QCA vendor attribute to indicate NDP interface managemtn using nl80211
a9c90475b FT: Update current_bss to target AP before check for SME-in-driver
0c88d1487 Debug print on CONFIG_NO_TKIP=y prevent RSNE with TKIP as group cipher
d5a9331f9 P2P: Copy only valid opclasses while filtering out 6 GHz channels
99c91beaa Sync with wireless-next.git include/uapi/linux/nl80211.h
d9121335a wpa_cli: Add ACL and BTM control commands
00622fcfe Extend ACL to install allow/deny list to the driver dynamically
077bce96f Set drv_max_acl_mac_addrs in wpa_supplicant AP mode
9828aba16 Support ACL operations in wpa_supplicant AP mode
fd0d738ff Add return value to ACL functions
f5ac42811 Move ACL control interface commands into shared files
930695662 Add BSS-TM-QUERY event to indicate reception of BSS TM Query
febcdf324 Support BTM operations in wpa_supplicant AP mode
0f8c6e995 Move BTM control interface commands into shared file
e059d8ece Update the Extended Capability element to struct sta_info
eb2e6b56b Enable BSS Transition Management in wpa_supplicant AP mode
30ecf0181 DPP: Update Controller parameters when it was already started
b93d1083e DPP: Fix msg_ctx for PKEX over TCP as Controller/Responder
3085e1a67 hs20-osu-client: dNSName values from OSU server certificate for PPS MO
ce86f2446 DFS: Remove unnecessary variable
760a5ae26 DFS: Switch to background radar channel if available
b63d953fe DFS: Enable CSA for background radar detection
25663241c DFS: Introduce hostapd_dfs_request_channel_switch()
316a9dc63 DFS: Configure background radar/CAC detection
bad12effe nl80211: Radar background flag setting
effd6111b DFS: Rely on channel_type in dfs_downgrade_bandwidth()
f9ba3d5c8 OpenSSL 3.0: Set SSL groups using SSL_set1_groups()
09c62aaf1 OpenSSL: Determine RSA key size without low-level routines
b700a56e1 OpenSSL 3.0: Determine the prime length for an EC key group using EVP_PKEY
3c61f4db4 OpenSSL: Replace EC_GROUP_get_curve_GFp() calls with EC_GROUP_get_curve()
e2cb0ca1a OpenSSL 3.0: Implement crypto_ec_key_group() with new API
f6a53f64a OpenSSL: Replace EVP_PKEY_cmp() with EVP_PKEY_eq() when available
5b093570d D-Bus: Add 'wep_disabled' capability
56a14cc72 DFS: Don't let cac_time_left_seconds overflow
ae512c30a DPP: Fix uninitialised variable on error path
3a157fe92 dbus: Set CurrentAuthMode to INACTIVE only if network is not selected
0ce8d55a2 hs20-osu-client: Allow EST server to use different host name
5eaf596e1 HTTP: Make URL available to the cert_cb
abed7978f HS 2.0 server: Event log entry on missing configuration for the realm
1192d5721 Android: Compile hs20-osu-client to /vendor/bin in test builds
1fee1c40c Enhance QCA vendor interface to indicate TWT required capability of AP
a192305a4 Add QCA vendor attributes for AFC support in external ACS
de5939ef5 DPP: Allow Configurator net_access_key_curve to be changed
9638452a6 DPP: Update Configurator to require same netAccessKey curve to be used
2b406eece DPP: Update Auth-I derivation operations
de64dfe98 DPP: Curve change for netAccessKey
fd2eb7a41 DPP: Fix a memory leak on error path
e9551efe0 DPP: Missing/invalid Protocol Version in Reconfig Auth Req
eeb72e7c9 DPP: Extend DPP_PKEX_ADD ver=<1/2> to cover Responder role
6c3c431bb Add QCA vendor attribute to enable Spectral FFT recapture
fcbdaae8a SAE: Add support for RADIUS passphrase as the SAE password
3d86fcee0 cleanup: Remove unreachable code
9683195ee qca-vendor: Fix typos
4c9ef9322 brcm_vendor: Fix typos
d65285ab8 src/drivers: Fix typos
203a027b2 nl80211: Report background radar/CAC detection capability
0a73649b6 DFS: Add capability to select radar-only channels
f39765369 DFS: Introduce dfs_set_valid_channel() utility routine
d001b301b Fix removal of wpa_passphrase on 'make clean'
cb41c214b build: Re-enable options for libwpa_client.so and wpa_passphrase
dec626109 HE: Fix invalid length checking for HE Capability element
53be64f7d HE: Fix calculation of the PPE Threshold field length
738fef2f0 Clear PSK explicitly from memory in couple more cases on deinit
567b9764f Clear PMK explicitly even without FT support in AP build
0bd29c176 Remove duplicated pointer check
007fd6111 Clear temporary results from stack in PBKDF2-SHA1
1364f322b Remove GTK/IGTK/BIGTK from memory explicitly in AP mode
af1f0694e Clear last set keys (for testing purposes) from memory explicitly
6c850a1c0 nl80211: Clear bss->freq when stopping AP mode
a44fa15cb Define a vendor specific NDP attribute for NAN service id
414ca953f DPP: Clear SCANNING state when starting network introduction
0b5f8e3d8 DPP: Clear netrole on starting chirping or reconfiguration
2fcc076d1 Clear wpa_s->last/current_ssid in more cases
7a7f803a9 DPP: Stop offchannel frame TX wait on DPP_STOP_LISTEN in a corner case
7e941e7a1 macsec_linux: Support cipher suite configuration
46c635910 MACsec: Support GCM-AES-256 cipher suite
42944de69 nl80211: Do not store no-wait TX frame cookies to be cancelled
340ec48cd DPP: Clear state on configuration failure in GAS server hander
7e6f59c70 nl80211: Clear the last saved TX frame cookie on wait expiration
9d5fd3328 Update QCA vendor attribute to indicate maximum PCL attributes
19169a53a atheros: Do not include p2p.h
f43d31dda nl80211: Debug print association comeback event data
a91072503 OCV: Don't start SA Query timer on CSA when SA Query is offloaded
f5c8697c0 Sync with mac80211-next.git include/uapi/linux/nl80211.h
632a9995c Clear ignore_old_scan_res on FLUSH command
Change-Id: I35fd1fb999d045ced8c153fe3d8284c9a71069b1
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 0030edc..faaedbf 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -540,6 +540,10 @@
if (!acs_usable_chan(chan))
continue;
+ if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+ iface->conf->acs_exclude_dfs)
+ continue;
+
if (!is_in_chanlist(iface, chan))
continue;
@@ -670,6 +674,10 @@
if (!chan_pri_allowed(chan))
continue;
+ if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+ iface->conf->acs_exclude_dfs)
+ continue;
+
if (!is_in_chanlist(iface, chan))
continue;
@@ -1044,7 +1052,9 @@
for (i = 0; i < mode->num_channels; i++) {
chan = &mode->channels[i];
- if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ if ((chan->flag & HOSTAPD_CHAN_DISABLED) ||
+ ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+ iface->conf->acs_exclude_dfs))
continue;
if (!is_in_chanlist(iface, chan))
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 33c68d4..db86a76 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1,6 +1,6 @@
/*
* hostapd / Configuration helper functions
- * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -463,9 +463,12 @@
wpa_hexdump_ascii_key(MSG_DEBUG, "PSK (ASCII passphrase)",
(u8 *) ssid->wpa_passphrase,
os_strlen(ssid->wpa_passphrase));
- pbkdf2_sha1(ssid->wpa_passphrase,
- ssid->ssid, ssid->ssid_len,
- 4096, ssid->wpa_psk->psk, PMK_LEN);
+ if (pbkdf2_sha1(ssid->wpa_passphrase,
+ ssid->ssid, ssid->ssid_len,
+ 4096, ssid->wpa_psk->psk, PMK_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "Error in pbkdf2_sha1()");
+ return -1;
+ }
wpa_hexdump_key(MSG_DEBUG, "PSK (from passphrase)",
ssid->wpa_psk->psk, PMK_LEN);
return 0;
@@ -811,6 +814,7 @@
os_free(conf->eap_fast_a_id);
os_free(conf->eap_fast_a_id_info);
os_free(conf->eap_sim_db);
+ os_free(conf->imsi_privacy_key);
os_free(conf->radius_server_clients);
os_free(conf->radius);
os_free(conf->radius_das_shared_secret);
@@ -1245,15 +1249,18 @@
if (full_config && bss->wpa &&
bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
+ bss->wpa_psk_radius != PSK_RADIUS_DURING_4WAY_HS &&
bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no "
"RADIUS checking (macaddr_acl=2) enabled.");
return -1;
}
- if (full_config && bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) &&
+ if (full_config && bss->wpa &&
+ wpa_key_mgmt_wpa_psk_no_sae(bss->wpa_key_mgmt) &&
bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL &&
bss->ssid.wpa_psk_file == NULL &&
+ bss->wpa_psk_radius != PSK_RADIUS_DURING_4WAY_HS &&
(bss->wpa_psk_radius != PSK_RADIUS_REQUIRED ||
bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) {
wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase "
@@ -1426,7 +1433,7 @@
#endif /* CONFIG_SAE_PK */
#ifdef CONFIG_FILS
- if (full_config && bss->fils_discovery_min_int &&
+ if (full_config && bss->fils_discovery_max_int &&
bss->unsol_bcast_probe_resp_interval) {
wpa_printf(MSG_ERROR,
"Cannot enable both FILS discovery and unsolicited broadcast Probe Response at the same time");
@@ -1434,6 +1441,14 @@
}
#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211BE
+ if (full_config && !bss->disable_11be && bss->disable_11ax) {
+ bss->disable_11be = true;
+ wpa_printf(MSG_INFO,
+ "Disabling IEEE 802.11be as IEEE 802.11ax is disabled for this BSS");
+ }
+#endif /* CONFIG_IEEE80211BE */
+
return 0;
}
@@ -1465,6 +1480,13 @@
{
size_t i;
+ if (full_config && is_6ghz_op_class(conf->op_class) &&
+ !conf->hw_mode_set) {
+ /* Use the appropriate hw_mode value automatically when the
+ * op_class parameter has been set, but hw_mode was not. */
+ conf->hw_mode = HOSTAPD_MODE_IEEE80211A;
+ }
+
if (full_config && conf->ieee80211d &&
(!conf->country[0] || !conf->country[1])) {
wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without "
@@ -1502,6 +1524,14 @@
return -1;
}
+#ifdef CONFIG_IEEE80211BE
+ if (full_config && conf->ieee80211be && !conf->ieee80211ax) {
+ wpa_printf(MSG_ERROR,
+ "Cannot set ieee80211be without ieee80211ax");
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
for (i = 0; i < conf->num_bss; i++) {
if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
return -1;
@@ -1648,3 +1678,49 @@
return with_pk;
}
#endif /* CONFIG_SAE_PK */
+
+
+int hostapd_acl_comp(const void *a, const void *b)
+{
+ const struct mac_acl_entry *aa = a;
+ const struct mac_acl_entry *bb = b;
+ return os_memcmp(aa->addr, bb->addr, sizeof(macaddr));
+}
+
+
+int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
+ int vlan_id, const u8 *addr)
+{
+ struct mac_acl_entry *newacl;
+
+ newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
+ if (!newacl) {
+ wpa_printf(MSG_ERROR, "MAC list reallocation failed");
+ return -1;
+ }
+
+ *acl = newacl;
+ os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
+ os_memset(&(*acl)[*num].vlan_id, 0, sizeof((*acl)[*num].vlan_id));
+ (*acl)[*num].vlan_id.untagged = vlan_id;
+ (*acl)[*num].vlan_id.notempty = !!vlan_id;
+ (*num)++;
+
+ return 0;
+}
+
+
+void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
+ const u8 *addr)
+{
+ int i = 0;
+
+ while (i < *num) {
+ if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) {
+ os_remove_in_array(*acl, *num, sizeof(**acl), i);
+ (*num)--;
+ } else {
+ i++;
+ }
+ }
+}
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index c1a0f71..b97d49c 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-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -20,6 +20,12 @@
#include "fst/fst.h"
#include "vlan.h"
+enum macaddr_acl {
+ ACCEPT_UNLESS_DENIED = 0,
+ DENY_UNLESS_ACCEPTED = 1,
+ USE_EXTERNAL_RADIUS_AUTH = 2
+};
+
/**
* mesh_conf - local MBSS state and settings
*/
@@ -331,12 +337,11 @@
int eap_reauth_period;
int erp_send_reauth_start;
char *erp_domain;
+#ifdef CONFIG_TESTING_OPTIONS
+ bool eap_skip_prot_success;
+#endif /* CONFIG_TESTING_OPTIONS */
- enum macaddr_acl {
- ACCEPT_UNLESS_DENIED = 0,
- DENY_UNLESS_ACCEPTED = 1,
- USE_EXTERNAL_RADIUS_AUTH = 2
- } macaddr_acl;
+ enum macaddr_acl macaddr_acl;
struct mac_acl_entry *accept_mac;
int num_accept_mac;
struct mac_acl_entry *deny_mac;
@@ -364,7 +369,8 @@
enum {
PSK_RADIUS_IGNORED = 0,
PSK_RADIUS_ACCEPTED = 1,
- PSK_RADIUS_REQUIRED = 2
+ PSK_RADIUS_REQUIRED = 2,
+ PSK_RADIUS_DURING_4WAY_HS = 3,
} wpa_psk_radius;
int wpa_pairwise;
int group_cipher; /* wpa_group value override from configuation */
@@ -439,6 +445,7 @@
int eap_teap_id;
int eap_sim_aka_result_ind;
int eap_sim_id;
+ char *imsi_privacy_key;
int tnc;
int fragment_size;
u16 pwd_group;
@@ -537,6 +544,7 @@
bool disable_11n;
bool disable_11ac;
bool disable_11ax;
+ bool disable_11be;
/* IEEE 802.11v */
int time_advertisement;
@@ -849,6 +857,13 @@
int mka_priority;
/**
+ * macsec_csindex - Cipher suite index for MACsec
+ *
+ * Range: 0-1 (default: 0)
+ */
+ int macsec_csindex;
+
+ /**
* mka_ckn - MKA pre-shared CKN
*/
#define MACSEC_CKN_MAX_LEN 32
@@ -937,6 +952,15 @@
};
/**
+ * struct eht_phy_capabilities_info - EHT PHY capabilities
+ */
+struct eht_phy_capabilities_info {
+ bool su_beamformer;
+ bool su_beamformee;
+ bool mu_beamformer;
+};
+
+/**
* struct hostapd_config - Per-radio interface configuration
*/
struct hostapd_config {
@@ -957,7 +981,9 @@
int acs_exclude_dfs;
u8 min_tx_power;
enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
+ bool hw_mode_set;
int acs_exclude_6ghz_non_psc;
+ int enable_background_radar;
enum {
LONG_PREAMBLE = 0,
SHORT_PREAMBLE = 1
@@ -1075,6 +1101,7 @@
u8 he_6ghz_max_ampdu_len_exp;
u8 he_6ghz_rx_ant_pat;
u8 he_6ghz_tx_ant_pat;
+ u8 he_6ghz_reg_pwr_type;
#endif /* CONFIG_IEEE80211AX */
/* VHT enable/disable config from CHAN_SWITCH */
@@ -1102,11 +1129,27 @@
unsigned int airtime_update_interval;
#define AIRTIME_MODE_MAX (__AIRTIME_MODE_MAX - 1)
#endif /* CONFIG_AIRTIME_POLICY */
+
+ int ieee80211be;
+#ifdef CONFIG_IEEE80211BE
+ u8 eht_oper_chwidth;
+ u8 eht_oper_centr_freq_seg0_idx;
+ struct eht_phy_capabilities_info eht_phy_capab;
+#endif /* CONFIG_IEEE80211BE */
+
+ /* EHT enable/disable config from CHAN_SWITCH */
+#define CH_SWITCH_EHT_ENABLED BIT(0)
+#define CH_SWITCH_EHT_DISABLED BIT(1)
+ unsigned int ch_switch_eht_config;
};
static inline u8 hostapd_get_oper_chwidth(struct hostapd_config *conf)
{
+#ifdef CONFIG_IEEE80211BE
+ if (conf->ieee80211be)
+ return conf->eht_oper_chwidth;
+#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_IEEE80211AX
if (conf->ieee80211ax)
return conf->he_oper_chwidth;
@@ -1117,6 +1160,10 @@
static inline void
hostapd_set_oper_chwidth(struct hostapd_config *conf, u8 oper_chwidth)
{
+#ifdef CONFIG_IEEE80211BE
+ if (conf->ieee80211be)
+ conf->eht_oper_chwidth = oper_chwidth;
+#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_IEEE80211AX
if (conf->ieee80211ax)
conf->he_oper_chwidth = oper_chwidth;
@@ -1127,6 +1174,10 @@
static inline u8
hostapd_get_oper_centr_freq_seg0_idx(struct hostapd_config *conf)
{
+#ifdef CONFIG_IEEE80211BE
+ if (conf->ieee80211be)
+ return conf->eht_oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_IEEE80211AX
if (conf->ieee80211ax)
return conf->he_oper_centr_freq_seg0_idx;
@@ -1138,6 +1189,10 @@
hostapd_set_oper_centr_freq_seg0_idx(struct hostapd_config *conf,
u8 oper_centr_freq_seg0_idx)
{
+#ifdef CONFIG_IEEE80211BE
+ if (conf->ieee80211be)
+ conf->eht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_IEEE80211AX
if (conf->ieee80211ax)
conf->he_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
@@ -1197,5 +1252,10 @@
bool hostapd_sae_pk_in_use(struct hostapd_bss_config *conf);
bool hostapd_sae_pk_exclusively(struct hostapd_bss_config *conf);
int hostapd_setup_sae_pt(struct hostapd_bss_config *conf);
+int hostapd_acl_comp(const void *a, const void *b);
+int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
+ int vlan_id, const u8 *addr);
+void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
+ const u8 *addr);
#endif /* HOSTAPD_CONFIG_H */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index e917736..8af7a0e 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -418,6 +418,8 @@
const struct ieee80211_vht_capabilities *vht_capab,
const struct ieee80211_he_capabilities *he_capab,
size_t he_capab_len,
+ const struct ieee80211_eht_capabilities *eht_capab,
+ size_t eht_capab_len,
const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
int set)
@@ -440,6 +442,8 @@
params.vht_capabilities = vht_capab;
params.he_capab = he_capab;
params.he_capab_len = he_capab_len;
+ params.eht_capab = eht_capab;
+ params.eht_capab_len = eht_capab_len;
params.he_6ghz_capab = he_6ghz_capab;
params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
params.vht_opmode = vht_opmode;
@@ -547,7 +551,7 @@
int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
int freq, int channel, int edmg, u8 edmg_channel,
int ht_enabled, int vht_enabled,
- int he_enabled,
+ int he_enabled, bool eht_enabled,
int sec_channel_offset, int oper_chwidth,
int center_segment0, int center_segment1)
{
@@ -556,12 +560,15 @@
if (hostapd_set_freq_params(&data, mode, freq, channel, edmg,
edmg_channel, ht_enabled,
- vht_enabled, he_enabled, sec_channel_offset,
- oper_chwidth,
+ vht_enabled, he_enabled, eht_enabled,
+ sec_channel_offset, oper_chwidth,
center_segment0, center_segment1,
cmode ? cmode->vht_capab : 0,
cmode ?
- &cmode->he_capab[IEEE80211_MODE_AP] : NULL))
+ &cmode->he_capab[IEEE80211_MODE_AP] : NULL,
+ cmode ?
+ &cmode->eht_capab[IEEE80211_MODE_AP] :
+ NULL))
return -1;
if (hapd->driver == NULL)
@@ -810,9 +817,10 @@
int hostapd_start_dfs_cac(struct hostapd_iface *iface,
enum hostapd_hw_mode mode, int freq,
int channel, int ht_enabled, int vht_enabled,
- int he_enabled,
+ int he_enabled, bool eht_enabled,
int sec_channel_offset, int oper_chwidth,
- int center_segment0, int center_segment1)
+ int center_segment0, int center_segment1,
+ bool radar_background)
{
struct hostapd_data *hapd = iface->bss[0];
struct hostapd_freq_params data;
@@ -830,18 +838,24 @@
if (hostapd_set_freq_params(&data, mode, freq, channel, 0, 0,
ht_enabled,
- vht_enabled, he_enabled, sec_channel_offset,
+ vht_enabled, he_enabled, eht_enabled,
+ sec_channel_offset,
oper_chwidth, center_segment0,
center_segment1,
cmode->vht_capab,
- &cmode->he_capab[IEEE80211_MODE_AP])) {
+ &cmode->he_capab[IEEE80211_MODE_AP],
+ &cmode->eht_capab[IEEE80211_MODE_AP])) {
wpa_printf(MSG_ERROR, "Can't set freq params");
return -1;
}
+ data.radar_background = radar_background;
res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
if (!res) {
- iface->cac_started = 1;
+ if (radar_background)
+ iface->radar_background.cac_started = 1;
+ else
+ iface->cac_started = 1;
os_get_reltime(&iface->dfs_cac_start);
}
@@ -953,13 +967,15 @@
params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
params.vht_enabled = !!(hapd->iface->conf->ieee80211ac);
+ params.eht_enabled = !!(hapd->iface->conf->ieee80211be);
params.ch_width = 20;
if (hapd->iface->conf->ieee80211n && params.ht40_enabled)
params.ch_width = 40;
/* Note: VHT20 is defined by combination of ht_capab & oper_chwidth
*/
- if ((hapd->iface->conf->ieee80211ax ||
+ if ((hapd->iface->conf->ieee80211be ||
+ hapd->iface->conf->ieee80211ax ||
hapd->iface->conf->ieee80211ac) &&
params.ht40_enabled) {
u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iface->conf);
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 61c8f64..b4fb766 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -43,6 +43,8 @@
const struct ieee80211_vht_capabilities *vht_capab,
const struct ieee80211_he_capabilities *he_capab,
size_t he_capab_len,
+ const struct ieee80211_eht_capabilities *eht_capab,
+ size_t eht_capab_len,
const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
int set);
@@ -64,8 +66,8 @@
int hostapd_flush(struct hostapd_data *hapd);
int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
int freq, int channel, int edmg, u8 edmg_channel,
- int ht_enabled, int vht_enabled,
- int he_enabled, int sec_channel_offset, int oper_chwidth,
+ int ht_enabled, int vht_enabled, int he_enabled,
+ bool eht_enabled, int sec_channel_offset, int oper_chwidth,
int center_segment0, int center_segment1);
int hostapd_set_rts(struct hostapd_data *hapd, int rts);
int hostapd_set_frag(struct hostapd_data *hapd, int frag);
@@ -128,9 +130,10 @@
int hostapd_start_dfs_cac(struct hostapd_iface *iface,
enum hostapd_hw_mode mode, int freq,
int channel, int ht_enabled, int vht_enabled,
- int he_enabled,
+ int he_enabled, bool eht_enabled,
int sec_channel_offset, int oper_chwidth,
- int center_segment0, int center_segment1);
+ int center_segment0, int center_segment1,
+ bool radar_background);
int hostapd_drv_do_acs(struct hostapd_data *hapd);
int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
u16 reason_code, const u8 *ie, size_t ielen);
@@ -299,6 +302,17 @@
return hapd->driver->switch_channel(hapd->drv_priv, settings);
}
+#ifdef CONFIG_IEEE80211AX
+static inline int hostapd_drv_switch_color(struct hostapd_data *hapd,
+ struct cca_settings *settings)
+{
+ if (!hapd->driver || !hapd->driver->switch_color || !hapd->drv_priv)
+ return -1;
+
+ return hapd->driver->switch_color(hapd->drv_priv, settings);
+}
+#endif /* CONFIG_IEEE80211AX */
+
static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf,
size_t buflen)
{
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index 8e12daf..fd9c96f 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -9,6 +9,7 @@
#include "utils/includes.h"
#include "utils/common.h"
+#include "crypto/crypto.h"
#include "crypto/tls.h"
#include "eap_server/eap.h"
#include "eap_server/eap_sim_db.h"
@@ -168,6 +169,9 @@
wpa_printf(MSG_DEBUG, "authsrv: remote TLS alert: %s",
data->alert.description);
break;
+ case TLS_UNSAFE_RENEGOTIATION_DISABLED:
+ /* Not applicable to TLS server */
+ break;
}
}
#endif /* EAP_TLS_FUNCS */
@@ -209,6 +213,7 @@
cfg->eap_teap_id = hapd->conf->eap_teap_id;
cfg->eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
cfg->eap_sim_id = hapd->conf->eap_sim_id;
+ cfg->imsi_privacy_key = hapd->imsi_privacy_key;
cfg->tnc = hapd->conf->tnc;
cfg->wps = hapd->wps;
cfg->fragment_size = hapd->conf->fragment_size;
@@ -222,6 +227,9 @@
cfg->server_id_len = 7;
}
cfg->erp = hapd->conf->eap_server_erp;
+#ifdef CONFIG_TESTING_OPTIONS
+ cfg->skip_prot_success = hapd->conf->eap_skip_prot_success;
+#endif /* CONFIG_TESTING_OPTIONS */
return cfg;
}
@@ -292,6 +300,22 @@
}
#endif /* EAP_TLS_FUNCS */
+#ifdef CRYPTO_RSA_OAEP_SHA256
+ crypto_rsa_key_free(hapd->imsi_privacy_key);
+ hapd->imsi_privacy_key = NULL;
+ if (hapd->conf->imsi_privacy_key) {
+ hapd->imsi_privacy_key = crypto_rsa_key_read(
+ hapd->conf->imsi_privacy_key, true);
+ if (!hapd->imsi_privacy_key) {
+ wpa_printf(MSG_ERROR,
+ "Failed to read/parse IMSI privacy key %s",
+ hapd->conf->imsi_privacy_key);
+ authsrv_deinit(hapd);
+ return -1;
+ }
+ }
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
+
#ifdef EAP_SIM_DB
if (hapd->conf->eap_sim_db) {
hapd->eap_sim_db_priv =
@@ -332,6 +356,11 @@
hapd->radius_srv = NULL;
#endif /* RADIUS_SERVER */
+#ifdef CRYPTO_RSA_OAEP_SHA256
+ crypto_rsa_key_free(hapd->imsi_privacy_key);
+ hapd->imsi_privacy_key = NULL;
+#endif /* CRYPTO_RSA_OAEP_SHA256 */
+
#ifdef EAP_TLS_FUNCS
if (hapd->ssl_ctx) {
tls_deinit(hapd->ssl_ctx);
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 8cd1c41..eaa4033 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -186,7 +186,8 @@
}
-static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing,
+static u8 * hostapd_eid_country_add(struct hostapd_data *hapd, u8 *pos,
+ u8 *end, int chan_spacing,
struct hostapd_channel_data *start,
struct hostapd_channel_data *prev)
{
@@ -198,31 +199,23 @@
/* number of channels */
*pos++ = (prev->chan - start->chan) / chan_spacing + 1;
/* maximum transmit power level */
- *pos++ = start->max_tx_power;
+ if (!is_6ghz_op_class(hapd->iconf->op_class))
+ *pos++ = start->max_tx_power;
+ else
+ *pos++ = 0; /* Reserved when operating on the 6 GHz band */
return pos;
}
-static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
- int max_len)
+static u8 * hostapd_fill_subband_triplets(struct hostapd_data *hapd, u8 *pos,
+ u8 *end)
{
- u8 *pos = eid;
- u8 *end = eid + max_len;
int i;
struct hostapd_hw_modes *mode;
struct hostapd_channel_data *start, *prev;
int chan_spacing = 1;
- if (!hapd->iconf->ieee80211d || max_len < 6 ||
- hapd->iface->current_mode == NULL)
- return eid;
-
- *pos++ = WLAN_EID_COUNTRY;
- pos++; /* length will be set later */
- os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
- pos += 3;
-
mode = hapd->iface->current_mode;
if (mode->mode == HOSTAPD_MODE_IEEE80211A)
chan_spacing = 4;
@@ -240,7 +233,8 @@
}
if (start && prev) {
- pos = hostapd_eid_country_add(pos, end, chan_spacing,
+ pos = hostapd_eid_country_add(hapd, pos, end,
+ chan_spacing,
start, prev);
start = NULL;
}
@@ -250,10 +244,50 @@
}
if (start) {
- pos = hostapd_eid_country_add(pos, end, chan_spacing,
+ pos = hostapd_eid_country_add(hapd, pos, end, chan_spacing,
start, prev);
}
+ return pos;
+}
+
+
+static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
+ int max_len)
+{
+ u8 *pos = eid;
+ u8 *end = eid + max_len;
+
+ if (!hapd->iconf->ieee80211d || max_len < 6 ||
+ hapd->iface->current_mode == NULL)
+ return eid;
+
+ *pos++ = WLAN_EID_COUNTRY;
+ pos++; /* length will be set later */
+ os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
+ pos += 3;
+
+ if (is_6ghz_op_class(hapd->iconf->op_class)) {
+ /* Force the third octet of the country string to indicate
+ * Global Operating Class (Table E-4) */
+ eid[4] = 0x04;
+
+ /* Operating Triplet field */
+ /* Operating Extension Identifier (>= 201 to indicate this is
+ * not a Subband Triplet field) */
+ *pos++ = 201;
+ /* Operating Class */
+ *pos++ = hapd->iconf->op_class;
+ /* Coverage Class */
+ *pos++ = 0;
+ /* Subband Triplets are required only for the 20 MHz case */
+ if (hapd->iconf->op_class == 131 ||
+ hapd->iconf->op_class == 136)
+ pos = hostapd_fill_subband_triplets(hapd, pos, end);
+ } else {
+ pos = hostapd_fill_subband_triplets(hapd, pos, end);
+ }
+
if ((pos - eid) & 1) {
if (end - pos < 1)
return eid;
@@ -463,12 +497,25 @@
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))
+ 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 */
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
+ buflen += 3 + sizeof(struct ieee80211_eht_operation);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
buflen += hostapd_mbo_ie_len(hapd);
buflen += hostapd_eid_owe_trans_len(hapd);
@@ -578,14 +625,30 @@
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+ u8 *cca_pos;
+
pos = hostapd_eid_he_capab(hapd, pos, IEEE80211_MODE_AP);
pos = hostapd_eid_he_operation(hapd, pos);
+
+ /* 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;
+ pos = cca_pos;
+
pos = hostapd_eid_spatial_reuse(hapd, pos);
pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos);
pos = hostapd_eid_he_6ghz_band_cap(hapd, pos);
}
#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ pos = hostapd_eid_eht_capab(hapd, pos, IEEE80211_MODE_AP);
+ pos = hostapd_eid_eht_operation(hapd, pos);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_IEEE80211AC
if (hapd->conf->vendor_vht)
pos = hostapd_eid_vendor_vht(hapd, pos);
@@ -1319,6 +1382,15 @@
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 */
+
head = os_zalloc(total_len);
if (!head)
return NULL;
@@ -1391,6 +1463,9 @@
pos += buf_len;
}
+ if (is_6ghz_op_class(hapd->iconf->op_class))
+ pos = hostapd_eid_txpower_envelope(hapd, pos);
+
*len = pos - (u8 *) head;
wpa_hexdump(MSG_DEBUG, "FILS Discovery frame template",
head, pos - (u8 *) head);
@@ -1465,12 +1540,25 @@
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))
+ 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 */
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ tail_len += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
+ tail_len += 3 + sizeof(struct ieee80211_eht_operation);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
tail_len += hostapd_mbo_ie_len(hapd);
tail_len += hostapd_eid_owe_trans_len(hapd);
@@ -1600,15 +1688,32 @@
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
+ u8 *cca_pos;
+
tailpos = hostapd_eid_he_capab(hapd, tailpos,
IEEE80211_MODE_AP);
tailpos = hostapd_eid_he_operation(hapd, tailpos);
+
+ /* BSS Color Change Announcement element */
+ cca_pos = hostapd_eid_cca(hapd, tailpos);
+ if (cca_pos != tailpos)
+ hapd->cca_c_off_beacon = cca_pos - tail - 2;
+ tailpos = cca_pos;
+
tailpos = hostapd_eid_spatial_reuse(hapd, tailpos);
tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos);
tailpos = hostapd_eid_he_6ghz_band_cap(hapd, tailpos);
}
#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ tailpos = hostapd_eid_eht_capab(hapd, tailpos,
+ IEEE80211_MODE_AP);
+ tailpos = hostapd_eid_eht_operation(hapd, tailpos);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_IEEE80211AC
if (hapd->conf->vendor_vht)
tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
@@ -1845,12 +1950,14 @@
iconf->channel, iconf->enable_edmg,
iconf->edmg_channel, iconf->ieee80211n,
iconf->ieee80211ac, iconf->ieee80211ax,
+ iconf->ieee80211be,
iconf->secondary_channel,
hostapd_get_oper_chwidth(iconf),
hostapd_get_oper_centr_freq_seg0_idx(iconf),
hostapd_get_oper_centr_freq_seg1_idx(iconf),
cmode->vht_capab,
- &cmode->he_capab[IEEE80211_MODE_AP]) == 0)
+ &cmode->he_capab[IEEE80211_MODE_AP],
+ &cmode->eht_capab[IEEE80211_MODE_AP]) == 0)
params.freq = &freq;
res = hostapd_drv_set_ap(hapd, ¶ms);
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 1d8fb82..29b41f5 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -24,6 +24,7 @@
#include "ap_drv_ops.h"
#include "mbo_ap.h"
#include "taxonomy.h"
+#include "wnm_ap.h"
static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
@@ -724,15 +725,15 @@
} else {
/* CAC started and CAC time set - calculate remaining time */
struct os_reltime now;
- unsigned int left_time;
+ long left_time;
os_reltime_age(&iface->dfs_cac_start, &now);
- left_time = iface->dfs_cac_ms / 1000 - now.sec;
+ left_time = (long) iface->dfs_cac_ms / 1000 - now.sec;
ret = os_snprintf(buf + len, buflen - len,
"cac_time_seconds=%u\n"
- "cac_time_left_seconds=%u\n",
+ "cac_time_left_seconds=%lu\n",
iface->dfs_cac_ms / 1000,
- left_time);
+ left_time > 0 ? left_time : 0);
}
if (os_snprintf_error(buflen - len, ret))
return len;
@@ -746,6 +747,7 @@
"ieee80211n=%d\n"
"ieee80211ac=%d\n"
"ieee80211ax=%d\n"
+ "ieee80211be=%d\n"
"beacon_int=%u\n"
"dtim_period=%d\n",
iface->conf->channel,
@@ -758,12 +760,27 @@
!hapd->conf->disable_11ac,
iface->conf->ieee80211ax &&
!hapd->conf->disable_11ax,
+ iface->conf->ieee80211be &&
+ !hapd->conf->disable_11be,
iface->conf->beacon_int,
hapd->conf->dtim_period);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
+#ifdef CONFIG_IEEE80211BE
+ if (iface->conf->ieee80211be && !hapd->conf->disable_11be) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "eht_oper_chwidth=%d\n"
+ "eht_oper_centr_freq_seg0_idx=%d\n",
+ iface->conf->eht_oper_chwidth,
+ iface->conf->eht_oper_centr_freq_seg0_idx);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_IEEE80211AX
if (iface->conf->ieee80211ax && !hapd->conf->disable_11ax) {
ret = os_snprintf(buf + len, buflen - len,
@@ -918,6 +935,7 @@
settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
settings->freq_params.he_enabled = !!os_strstr(pos, " he");
+ settings->freq_params.eht_enabled = !!os_strstr(pos, " eht");
settings->block_tx = !!os_strstr(pos, " blocktx");
#undef SET_CSA_SETTING
@@ -1047,3 +1065,351 @@
#endif /* CONFIG_MESH */
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+
+#ifdef CONFIG_WNM_AP
+
+int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ int disassoc_timer;
+ struct sta_info *sta;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+ if (cmd[17] != ' ')
+ return -1;
+ disassoc_timer = atoi(cmd + 17);
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for disassociation imminent message",
+ MAC2STR(addr));
+ return -1;
+ }
+
+ return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer);
+}
+
+
+int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ const char *url, *timerstr;
+ int disassoc_timer;
+ struct sta_info *sta;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for ESS disassociation imminent message",
+ MAC2STR(addr));
+ return -1;
+ }
+
+ timerstr = cmd + 17;
+ if (*timerstr != ' ')
+ return -1;
+ timerstr++;
+ disassoc_timer = atoi(timerstr);
+ if (disassoc_timer < 0 || disassoc_timer > 65535)
+ return -1;
+
+ url = os_strchr(timerstr, ' ');
+ if (url == NULL)
+ return -1;
+ url++;
+
+ return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
+}
+
+
+int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ const char *pos, *end;
+ int disassoc_timer = 0;
+ struct sta_info *sta;
+ u8 req_mode = 0, valid_int = 0x01, dialog_token = 0x01;
+ u8 bss_term_dur[12];
+ char *url = NULL;
+ int ret;
+ u8 nei_rep[1000];
+ int nei_len;
+ u8 mbo[10];
+ size_t mbo_len = 0;
+
+ if (hwaddr_aton(cmd, addr)) {
+ wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
+ return -1;
+ }
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG, "Station " MACSTR
+ " not found for BSS TM Request message",
+ MAC2STR(addr));
+ return -1;
+ }
+
+ pos = os_strstr(cmd, " disassoc_timer=");
+ if (pos) {
+ pos += 16;
+ disassoc_timer = atoi(pos);
+ if (disassoc_timer < 0 || disassoc_timer > 65535) {
+ wpa_printf(MSG_DEBUG, "Invalid disassoc_timer");
+ return -1;
+ }
+ }
+
+ pos = os_strstr(cmd, " valid_int=");
+ if (pos) {
+ pos += 11;
+ valid_int = atoi(pos);
+ }
+
+ pos = os_strstr(cmd, " dialog_token=");
+ if (pos) {
+ pos += 14;
+ dialog_token = atoi(pos);
+ }
+
+ pos = os_strstr(cmd, " bss_term=");
+ if (pos) {
+ pos += 10;
+ req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED;
+ /* TODO: TSF configurable/learnable */
+ bss_term_dur[0] = 4; /* Subelement ID */
+ bss_term_dur[1] = 10; /* Length */
+ os_memset(&bss_term_dur[2], 0, 8);
+ end = os_strchr(pos, ',');
+ if (end == NULL) {
+ wpa_printf(MSG_DEBUG, "Invalid bss_term data");
+ return -1;
+ }
+ end++;
+ WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
+ }
+
+ nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep,
+ sizeof(nei_rep));
+ if (nei_len < 0)
+ return -1;
+
+ pos = os_strstr(cmd, " url=");
+ if (pos) {
+ size_t len;
+ pos += 5;
+ end = os_strchr(pos, ' ');
+ if (end)
+ len = end - pos;
+ else
+ len = os_strlen(pos);
+ url = os_malloc(len + 1);
+ if (url == NULL)
+ return -1;
+ os_memcpy(url, pos, len);
+ url[len] = '\0';
+ req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+ }
+
+ if (os_strstr(cmd, " pref=1"))
+ req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
+ if (os_strstr(cmd, " abridged=1"))
+ req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
+ if (os_strstr(cmd, " disassoc_imminent=1"))
+ req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+
+#ifdef CONFIG_MBO
+ pos = os_strstr(cmd, "mbo=");
+ if (pos) {
+ unsigned int mbo_reason, cell_pref, reassoc_delay;
+ u8 *mbo_pos = mbo;
+
+ ret = sscanf(pos, "mbo=%u:%u:%u", &mbo_reason,
+ &reassoc_delay, &cell_pref);
+ if (ret != 3) {
+ wpa_printf(MSG_DEBUG,
+ "MBO requires three arguments: mbo=<reason>:<reassoc_delay>:<cell_pref>");
+ ret = -1;
+ goto fail;
+ }
+
+ if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid MBO transition reason code %u",
+ mbo_reason);
+ ret = -1;
+ goto fail;
+ }
+
+ /* Valid values for Cellular preference are: 0, 1, 255 */
+ if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid MBO cellular capability %u",
+ cell_pref);
+ ret = -1;
+ goto fail;
+ }
+
+ if (reassoc_delay > 65535 ||
+ (reassoc_delay &&
+ !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) {
+ wpa_printf(MSG_DEBUG,
+ "MBO: Assoc retry delay is only valid in disassoc imminent mode");
+ ret = -1;
+ goto fail;
+ }
+
+ *mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
+ *mbo_pos++ = 1;
+ *mbo_pos++ = mbo_reason;
+ *mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
+ *mbo_pos++ = 1;
+ *mbo_pos++ = cell_pref;
+
+ if (reassoc_delay) {
+ *mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
+ *mbo_pos++ = 2;
+ WPA_PUT_LE16(mbo_pos, reassoc_delay);
+ mbo_pos += 2;
+ }
+
+ mbo_len = mbo_pos - mbo;
+ }
+#endif /* CONFIG_MBO */
+
+ ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
+ valid_int, bss_term_dur, dialog_token, url,
+ nei_len ? nei_rep : NULL, nei_len,
+ mbo_len ? mbo : NULL, mbo_len);
+#ifdef CONFIG_MBO
+fail:
+#endif /* CONFIG_MBO */
+ os_free(url);
+ return ret;
+}
+
+#endif /* CONFIG_WNM_AP */
+
+
+int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
+ const char *txtaddr)
+{
+ u8 addr[ETH_ALEN];
+ struct vlan_description vlan_id;
+
+ if (!(*num))
+ return 0;
+
+ if (hwaddr_aton(txtaddr, addr))
+ return -1;
+
+ if (hostapd_maclist_found(*acl, *num, addr, &vlan_id))
+ hostapd_remove_acl_mac(acl, num, addr);
+
+ return 0;
+}
+
+
+void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl,
+ int *num)
+{
+ while (*num)
+ hostapd_remove_acl_mac(acl, num, (*acl)[0].addr);
+}
+
+
+int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num,
+ char *buf, size_t buflen)
+{
+ int i = 0, len = 0, ret = 0;
+
+ if (!acl)
+ return 0;
+
+ while (i < num) {
+ ret = os_snprintf(buf + len, buflen - len,
+ MACSTR " VLAN_ID=%d\n",
+ MAC2STR(acl[i].addr),
+ acl[i].vlan_id.untagged);
+ if (ret < 0 || (size_t) ret >= buflen - len)
+ return len;
+ i++;
+ len += ret;
+ }
+ return len;
+}
+
+
+int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num,
+ const char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ struct vlan_description vlan_id;
+ int ret = 0, vlanid = 0;
+ const char *pos;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ pos = os_strstr(cmd, "VLAN_ID=");
+ if (pos)
+ vlanid = atoi(pos + 8);
+
+ if (!hostapd_maclist_found(*acl, *num, addr, &vlan_id)) {
+ ret = hostapd_add_acl_maclist(acl, num, vlanid, addr);
+ if (ret != -1 && *acl)
+ qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
+ }
+
+ return ret < 0 ? -1 : 0;
+}
+
+
+int hostapd_disassoc_accept_mac(struct hostapd_data *hapd)
+{
+ struct sta_info *sta;
+ struct vlan_description vlan_id;
+
+ if (hapd->conf->macaddr_acl != DENY_UNLESS_ACCEPTED)
+ return 0;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!hostapd_maclist_found(hapd->conf->accept_mac,
+ hapd->conf->num_accept_mac,
+ sta->addr, &vlan_id) ||
+ (vlan_id.notempty &&
+ vlan_compare(&vlan_id, sta->vlan_desc)))
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_UNSPECIFIED);
+ }
+
+ return 0;
+}
+
+
+int hostapd_disassoc_deny_mac(struct hostapd_data *hapd)
+{
+ struct sta_info *sta;
+ struct vlan_description vlan_id;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (hostapd_maclist_found(hapd->conf->deny_mac,
+ hapd->conf->num_deny_mac, sta->addr,
+ &vlan_id) &&
+ (!vlan_id.notempty ||
+ !vlan_compare(&vlan_id, sta->vlan_desc)))
+ ap_sta_disconnect(hapd, sta, sta->addr,
+ WLAN_REASON_UNSPECIFIED);
+ }
+
+ return 0;
+}
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index d1dcebf..614f042 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -37,4 +37,21 @@
const u8 *addr, char *buf, size_t len);
void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd);
+int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
+ const char *cmd);
+int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
+ const char *cmd);
+int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+ const char *cmd);
+int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num,
+ const char *cmd);
+int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
+ const char *txtaddr);
+void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl,
+ int *num);
+int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num,
+ char *buf, size_t buflen);
+int hostapd_disassoc_accept_mac(struct hostapd_data *hapd);
+int hostapd_disassoc_deny_mac(struct hostapd_data *hapd);
+
#endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 5c99ecf..e46dd7e 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -19,6 +19,26 @@
#include "dfs.h"
+enum dfs_channel_type {
+ DFS_ANY_CHANNEL,
+ DFS_AVAILABLE, /* non-radar or radar-available */
+ DFS_NO_CAC_YET, /* radar-not-yet-available */
+};
+
+static struct hostapd_channel_data *
+dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
+ u8 *oper_centr_freq_seg0_idx,
+ u8 *oper_centr_freq_seg1_idx,
+ enum dfs_channel_type *channel_type);
+
+
+static bool dfs_use_radar_background(struct hostapd_iface *iface)
+{
+ return (iface->drv_flags2 & WPA_DRIVER_RADAR_BACKGROUND) &&
+ iface->conf->enable_background_radar;
+}
+
+
static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
{
int n_chans = 1;
@@ -51,15 +71,27 @@
}
+/* dfs_channel_available: select new channel according to type parameter */
static int dfs_channel_available(struct hostapd_channel_data *chan,
- int skip_radar)
+ enum dfs_channel_type type)
{
+ if (type == DFS_NO_CAC_YET) {
+ /* Select only radar channel where CAC has not been
+ * performed yet
+ */
+ if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+ (chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+ HOSTAPD_CHAN_DFS_USABLE)
+ return 1;
+ return 0;
+ }
+
/*
* When radar detection happens, CSA is performed. However, there's no
* time for CAC, so radar channels must be skipped when finding a new
* channel for CSA, unless they are available for immediate use.
*/
- if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) &&
+ if (type == DFS_AVAILABLE && (chan->flag & HOSTAPD_CHAN_RADAR) &&
((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
HOSTAPD_CHAN_DFS_AVAILABLE))
return 0;
@@ -138,7 +170,7 @@
static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
int first_chan_idx, int num_chans,
- int skip_radar)
+ enum dfs_channel_type type)
{
struct hostapd_channel_data *first_chan, *chan;
int i;
@@ -177,7 +209,7 @@
return 0;
}
- if (!dfs_channel_available(chan, skip_radar)) {
+ if (!dfs_channel_available(chan, type)) {
wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
first_chan->freq + i * 20);
return 0;
@@ -207,7 +239,7 @@
*/
static int dfs_find_channel(struct hostapd_iface *iface,
struct hostapd_channel_data **ret_chan,
- int idx, int skip_radar)
+ int idx, enum dfs_channel_type type)
{
struct hostapd_hw_modes *mode;
struct hostapd_channel_data *chan;
@@ -232,7 +264,7 @@
}
/* Skip incompatible chandefs */
- if (!dfs_chan_range_available(mode, i, n_chans, skip_radar)) {
+ if (!dfs_chan_range_available(mode, i, n_chans, type)) {
wpa_printf(MSG_DEBUG,
"DFS: range not available for %d (%d)",
chan->freq, chan->chan);
@@ -475,7 +507,7 @@
int *secondary_channel,
u8 *oper_centr_freq_seg0_idx,
u8 *oper_centr_freq_seg1_idx,
- int skip_radar)
+ enum dfs_channel_type type)
{
struct hostapd_hw_modes *mode;
struct hostapd_channel_data *chan = NULL;
@@ -499,7 +531,7 @@
return NULL;
/* Get the count first */
- num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
+ num_available_chandefs = dfs_find_channel(iface, NULL, 0, type);
wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d",
num_available_chandefs);
if (num_available_chandefs == 0)
@@ -508,7 +540,7 @@
if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
return NULL;
chan_idx = _rand % num_available_chandefs;
- dfs_find_channel(iface, &chan, chan_idx, skip_radar);
+ dfs_find_channel(iface, &chan, chan_idx, type);
if (!chan) {
wpa_printf(MSG_DEBUG, "DFS: no random channel found");
return NULL;
@@ -537,7 +569,7 @@
for (i = 0; i < num_available_chandefs - 1; i++) {
/* start from chan_idx + 1, end when chan_idx - 1 */
chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs;
- dfs_find_channel(iface, &chan2, chan_idx2, skip_radar);
+ dfs_find_channel(iface, &chan2, chan_idx2, type);
if (chan2 && abs(chan2->chan - chan->chan) > 12) {
/* two channels are not adjacent */
sec_chan_idx_80p80 = chan2->chan;
@@ -568,6 +600,30 @@
}
+static int dfs_set_valid_channel(struct hostapd_iface *iface, int skip_radar)
+{
+ struct hostapd_channel_data *channel;
+ u8 cf1 = 0, cf2 = 0;
+ int sec = 0;
+
+ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
+ skip_radar ? DFS_AVAILABLE :
+ DFS_ANY_CHANNEL);
+ if (!channel) {
+ wpa_printf(MSG_ERROR, "could not get valid channel");
+ return -1;
+ }
+
+ iface->freq = channel->freq;
+ iface->conf->channel = channel->chan;
+ iface->conf->secondary_channel = sec;
+ hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
+ hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
+
+ return 0;
+}
+
+
static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
{
struct hostapd_hw_modes *mode;
@@ -755,7 +811,6 @@
*/
int hostapd_handle_dfs(struct hostapd_iface *iface)
{
- struct hostapd_channel_data *channel;
int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
int skip_radar = 0;
@@ -810,28 +865,17 @@
wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
res, res ? "yes": "no");
if (res) {
- int sec = 0;
- u8 cf1 = 0, cf2 = 0;
-
- channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
- skip_radar);
- if (!channel) {
- wpa_printf(MSG_ERROR, "could not get valid channel");
+ if (dfs_set_valid_channel(iface, skip_radar) < 0) {
hostapd_set_state(iface, HAPD_IFACE_DFS);
return 0;
}
-
- iface->freq = channel->freq;
- iface->conf->channel = channel->chan;
- iface->conf->secondary_channel = sec;
- hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
- hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
}
} while (res);
/* Finally start CAC */
hostapd_set_state(iface, HAPD_IFACE_DFS);
- wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
+ wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz%s", iface->freq,
+ dfs_use_radar_background(iface) ? " (background)" : "");
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
"freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
iface->freq,
@@ -844,17 +888,41 @@
res = hostapd_start_dfs_cac(
iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
iface->conf->ieee80211n, iface->conf->ieee80211ac,
- iface->conf->ieee80211ax,
+ iface->conf->ieee80211ax, iface->conf->ieee80211be,
iface->conf->secondary_channel,
hostapd_get_oper_chwidth(iface->conf),
hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
- hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+ dfs_use_radar_background(iface));
if (res) {
wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
return -1;
}
+ if (dfs_use_radar_background(iface)) {
+ /* Cache background radar parameters. */
+ iface->radar_background.channel = iface->conf->channel;
+ iface->radar_background.secondary_channel =
+ iface->conf->secondary_channel;
+ iface->radar_background.freq = iface->freq;
+ iface->radar_background.centr_freq_seg0_idx =
+ hostapd_get_oper_centr_freq_seg0_idx(iface->conf);
+ iface->radar_background.centr_freq_seg1_idx =
+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf);
+
+ /*
+ * Let's select a random channel according to the
+ * regulations and perform CAC on dedicated radar chain.
+ */
+ res = dfs_set_valid_channel(iface, 1);
+ if (res < 0)
+ return res;
+
+ iface->radar_background.temp_ch = 1;
+ return 1;
+ }
+
return 0;
}
@@ -876,6 +944,177 @@
}
+static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+ int channel, int freq,
+ int secondary_channel,
+ u8 current_vht_oper_chwidth,
+ u8 oper_centr_freq_seg0_idx,
+ u8 oper_centr_freq_seg1_idx)
+{
+ struct hostapd_hw_modes *cmode = iface->current_mode;
+ int ieee80211_mode = IEEE80211_MODE_AP, err;
+ struct csa_settings csa_settings;
+ u8 new_vht_oper_chwidth;
+ unsigned int i;
+
+ wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
+ "freq=%d chan=%d sec_chan=%d", freq, channel,
+ secondary_channel);
+
+ new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+ hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth);
+
+ /* Setup CSA request */
+ os_memset(&csa_settings, 0, sizeof(csa_settings));
+ csa_settings.cs_count = 5;
+ csa_settings.block_tx = 1;
+#ifdef CONFIG_MESH
+ if (iface->mconf)
+ ieee80211_mode = IEEE80211_MODE_MESH;
+#endif /* CONFIG_MESH */
+ err = hostapd_set_freq_params(&csa_settings.freq_params,
+ iface->conf->hw_mode,
+ freq, channel,
+ iface->conf->enable_edmg,
+ iface->conf->edmg_channel,
+ iface->conf->ieee80211n,
+ iface->conf->ieee80211ac,
+ iface->conf->ieee80211ax,
+ iface->conf->ieee80211be,
+ secondary_channel,
+ new_vht_oper_chwidth,
+ oper_centr_freq_seg0_idx,
+ oper_centr_freq_seg1_idx,
+ cmode->vht_capab,
+ &cmode->he_capab[ieee80211_mode],
+ &cmode->eht_capab[ieee80211_mode]);
+
+ if (err) {
+ wpa_printf(MSG_ERROR,
+ "DFS failed to calculate CSA freq params");
+ hostapd_disable_iface(iface);
+ return err;
+ }
+
+ for (i = 0; i < iface->num_bss; i++) {
+ err = hostapd_switch_channel(iface->bss[i], &csa_settings);
+ if (err)
+ break;
+ }
+
+ if (err) {
+ wpa_printf(MSG_WARNING,
+ "DFS failed to schedule CSA (%d) - trying fallback",
+ err);
+ iface->freq = freq;
+ iface->conf->channel = channel;
+ iface->conf->secondary_channel = secondary_channel;
+ hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
+ hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+ oper_centr_freq_seg0_idx);
+ hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+ oper_centr_freq_seg1_idx);
+
+ hostapd_disable_iface(iface);
+ hostapd_enable_iface(iface);
+
+ return 0;
+ }
+
+ /* Channel configuration will be updated once CSA completes and
+ * ch_switch_notify event is received */
+ wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
+
+ return 0;
+}
+
+
+static void hostpad_dfs_update_background_chain(struct hostapd_iface *iface)
+{
+ int sec = 0;
+ enum dfs_channel_type channel_type = DFS_NO_CAC_YET;
+ struct hostapd_channel_data *channel;
+ u8 oper_centr_freq_seg0_idx = 0;
+ u8 oper_centr_freq_seg1_idx = 0;
+
+ /*
+ * Allow selection of DFS channel in ETSI to comply with
+ * uniform spreading.
+ */
+ if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
+ channel_type = DFS_ANY_CHANNEL;
+
+ channel = dfs_get_valid_channel(iface, &sec, &oper_centr_freq_seg0_idx,
+ &oper_centr_freq_seg1_idx,
+ channel_type);
+ if (!channel ||
+ channel->chan == iface->conf->channel ||
+ channel->chan == iface->radar_background.channel)
+ channel = dfs_downgrade_bandwidth(iface, &sec,
+ &oper_centr_freq_seg0_idx,
+ &oper_centr_freq_seg1_idx,
+ &channel_type);
+ if (!channel ||
+ hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
+ channel->freq, channel->chan,
+ iface->conf->ieee80211n,
+ iface->conf->ieee80211ac,
+ iface->conf->ieee80211ax,
+ iface->conf->ieee80211be,
+ sec, hostapd_get_oper_chwidth(iface->conf),
+ oper_centr_freq_seg0_idx,
+ oper_centr_freq_seg1_idx, true)) {
+ wpa_printf(MSG_ERROR, "DFS failed to start CAC offchannel");
+ iface->radar_background.channel = -1;
+ return;
+ }
+
+ iface->radar_background.channel = channel->chan;
+ iface->radar_background.freq = channel->freq;
+ iface->radar_background.secondary_channel = sec;
+ iface->radar_background.centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
+ iface->radar_background.centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
+
+ wpa_printf(MSG_DEBUG,
+ "%s: setting background chain to chan %d (%d MHz)",
+ __func__, channel->chan, channel->freq);
+}
+
+
+static bool
+hostapd_dfs_is_background_event(struct hostapd_iface *iface, int freq)
+{
+ return dfs_use_radar_background(iface) &&
+ iface->radar_background.channel != -1 &&
+ iface->radar_background.freq == freq;
+}
+
+
+static int
+hostapd_dfs_start_channel_switch_background(struct hostapd_iface *iface)
+{
+ u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+
+ iface->conf->channel = iface->radar_background.channel;
+ iface->freq = iface->radar_background.freq;
+ iface->conf->secondary_channel =
+ iface->radar_background.secondary_channel;
+ hostapd_set_oper_centr_freq_seg0_idx(
+ iface->conf, iface->radar_background.centr_freq_seg0_idx);
+ hostapd_set_oper_centr_freq_seg1_idx(
+ iface->conf, iface->radar_background.centr_freq_seg1_idx);
+
+ hostpad_dfs_update_background_chain(iface);
+
+ return hostapd_dfs_request_channel_switch(
+ iface, iface->conf->channel, iface->freq,
+ iface->conf->secondary_channel, current_vht_oper_chwidth,
+ hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+ hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
+}
+
+
int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
int ht_enabled, int chan_offset, int chan_width,
int cf1, int cf2)
@@ -896,6 +1135,22 @@
set_dfs_state(iface, freq, ht_enabled, chan_offset,
chan_width, cf1, cf2,
HOSTAPD_CHAN_DFS_AVAILABLE);
+
+ /*
+ * Radar event from background chain for the selected
+ * channel. Perform CSA, move the main chain to the
+ * selected channel and configure the background chain
+ * to a new DFS channel.
+ */
+ if (hostapd_dfs_is_background_event(iface, freq)) {
+ iface->radar_background.cac_started = 0;
+ if (!iface->radar_background.temp_ch)
+ return 0;
+
+ iface->radar_background.temp_ch = 0;
+ return hostapd_dfs_start_channel_switch_background(iface);
+ }
+
/*
* Just mark the channel available when CAC completion
* event is received in enabled state. CAC result could
@@ -912,6 +1167,9 @@
iface->cac_started = 0;
}
}
+ } else if (hostapd_dfs_is_background_event(iface, freq)) {
+ iface->radar_background.cac_started = 0;
+ hostpad_dfs_update_background_chain(iface);
}
return 0;
@@ -940,7 +1198,8 @@
static struct hostapd_channel_data *
dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
u8 *oper_centr_freq_seg0_idx,
- u8 *oper_centr_freq_seg1_idx, int *skip_radar)
+ u8 *oper_centr_freq_seg1_idx,
+ enum dfs_channel_type *channel_type)
{
struct hostapd_channel_data *channel;
@@ -948,22 +1207,22 @@
channel = dfs_get_valid_channel(iface, secondary_channel,
oper_centr_freq_seg0_idx,
oper_centr_freq_seg1_idx,
- *skip_radar);
+ *channel_type);
if (channel) {
wpa_printf(MSG_DEBUG, "DFS: Selected channel: %d",
channel->chan);
return channel;
}
- if (*skip_radar) {
- *skip_radar = 0;
+ if (*channel_type != DFS_ANY_CHANNEL) {
+ *channel_type = DFS_ANY_CHANNEL;
} else {
int oper_chwidth;
oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
if (oper_chwidth == CHANWIDTH_USE_HT)
break;
- *skip_radar = 1;
+ *channel_type = DFS_AVAILABLE;
hostapd_set_oper_chwidth(iface->conf, oper_chwidth - 1);
}
}
@@ -981,7 +1240,7 @@
int secondary_channel;
u8 oper_centr_freq_seg0_idx = 0;
u8 oper_centr_freq_seg1_idx = 0;
- int skip_radar = 0;
+ enum dfs_channel_type channel_type = DFS_ANY_CHANNEL;
int err = 1;
/* Radar detected during active CAC */
@@ -989,13 +1248,13 @@
channel = dfs_get_valid_channel(iface, &secondary_channel,
&oper_centr_freq_seg0_idx,
&oper_centr_freq_seg1_idx,
- skip_radar);
+ channel_type);
if (!channel) {
channel = dfs_downgrade_bandwidth(iface, &secondary_channel,
&oper_centr_freq_seg0_idx,
&oper_centr_freq_seg1_idx,
- &skip_radar);
+ &channel_type);
if (!channel) {
wpa_printf(MSG_ERROR, "No valid channel available");
return err;
@@ -1022,20 +1281,61 @@
}
+static int
+hostapd_dfs_background_start_channel_switch(struct hostapd_iface *iface,
+ int freq)
+{
+ if (!dfs_use_radar_background(iface))
+ return -1; /* Background radar chain not supported. */
+
+ wpa_printf(MSG_DEBUG,
+ "%s called (background CAC active: %s, CSA active: %s)",
+ __func__, iface->radar_background.cac_started ? "yes" : "no",
+ hostapd_csa_in_progress(iface) ? "yes" : "no");
+
+ /* Check if CSA in progress */
+ if (hostapd_csa_in_progress(iface))
+ return 0;
+
+ if (hostapd_dfs_is_background_event(iface, freq)) {
+ /*
+ * Radar pattern is reported on the background chain.
+ * Just select a new random channel according to the
+ * regulations for monitoring.
+ */
+ hostpad_dfs_update_background_chain(iface);
+ return 0;
+ }
+
+ /*
+ * If background radar detection is supported and the radar channel
+ * monitored by the background chain is available switch to it without
+ * waiting for the CAC.
+ */
+ if (iface->radar_background.channel == -1)
+ return -1; /* Background radar chain not available. */
+
+ if (iface->radar_background.cac_started) {
+ /*
+ * Background channel not available yet. Perform CAC on the
+ * main chain.
+ */
+ iface->radar_background.temp_ch = 1;
+ return -1;
+ }
+
+ return hostapd_dfs_start_channel_switch_background(iface);
+}
+
+
static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
{
struct hostapd_channel_data *channel;
int secondary_channel;
u8 oper_centr_freq_seg0_idx;
u8 oper_centr_freq_seg1_idx;
- u8 new_vht_oper_chwidth;
- int skip_radar = 1;
- struct csa_settings csa_settings;
- unsigned int i;
- int err = 1;
- struct hostapd_hw_modes *cmode = iface->current_mode;
+ enum dfs_channel_type channel_type = DFS_AVAILABLE;
u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
- int ieee80211_mode = IEEE80211_MODE_AP;
wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
__func__, iface->cac_started ? "yes" : "no",
@@ -1054,13 +1354,13 @@
* uniform spreading.
*/
if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
- skip_radar = 0;
+ channel_type = DFS_ANY_CHANNEL;
/* Perform channel switch/CSA */
channel = dfs_get_valid_channel(iface, &secondary_channel,
&oper_centr_freq_seg0_idx,
&oper_centr_freq_seg1_idx,
- skip_radar);
+ channel_type);
if (!channel) {
/*
@@ -1068,11 +1368,11 @@
* there is another channel where we can switch even if it
* requires to perform a CAC first.
*/
- skip_radar = 0;
+ channel_type = DFS_ANY_CHANNEL;
channel = dfs_downgrade_bandwidth(iface, &secondary_channel,
&oper_centr_freq_seg0_idx,
&oper_centr_freq_seg1_idx,
- &skip_radar);
+ &channel_type);
if (!channel) {
/*
* Toggle interface state to enter DFS state
@@ -1083,7 +1383,7 @@
return 0;
}
- if (!skip_radar) {
+ if (channel_type == DFS_ANY_CHANNEL) {
iface->freq = channel->freq;
iface->conf->channel = channel->chan;
iface->conf->secondary_channel = secondary_channel;
@@ -1098,73 +1398,12 @@
}
}
- wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
- channel->chan);
- wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
- "freq=%d chan=%d sec_chan=%d", channel->freq,
- channel->chan, secondary_channel);
-
- new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
- hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth);
-
- /* Setup CSA request */
- os_memset(&csa_settings, 0, sizeof(csa_settings));
- csa_settings.cs_count = 5;
- csa_settings.block_tx = 1;
-#ifdef CONFIG_MESH
- if (iface->mconf)
- ieee80211_mode = IEEE80211_MODE_MESH;
-#endif /* CONFIG_MESH */
- err = hostapd_set_freq_params(&csa_settings.freq_params,
- iface->conf->hw_mode,
- channel->freq,
- channel->chan,
- iface->conf->enable_edmg,
- iface->conf->edmg_channel,
- iface->conf->ieee80211n,
- iface->conf->ieee80211ac,
- iface->conf->ieee80211ax,
- secondary_channel,
- new_vht_oper_chwidth,
- oper_centr_freq_seg0_idx,
- oper_centr_freq_seg1_idx,
- cmode->vht_capab,
- &cmode->he_capab[ieee80211_mode]);
-
- if (err) {
- wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
- hostapd_disable_iface(iface);
- return err;
- }
-
- for (i = 0; i < iface->num_bss; i++) {
- err = hostapd_switch_channel(iface->bss[i], &csa_settings);
- if (err)
- break;
- }
-
- if (err) {
- wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
- err);
- iface->freq = channel->freq;
- iface->conf->channel = channel->chan;
- iface->conf->secondary_channel = secondary_channel;
- hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
- hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
- oper_centr_freq_seg0_idx);
- hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
- oper_centr_freq_seg1_idx);
-
- hostapd_disable_iface(iface);
- hostapd_enable_iface(iface);
- return 0;
- }
-
- /* Channel configuration will be updated once CSA completes and
- * ch_switch_notify event is received */
-
- wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
- return 0;
+ return hostapd_dfs_request_channel_switch(iface, channel->chan,
+ channel->freq,
+ secondary_channel,
+ current_vht_oper_chwidth,
+ oper_centr_freq_seg0_idx,
+ oper_centr_freq_seg1_idx);
}
@@ -1172,8 +1411,6 @@
int ht_enabled, int chan_offset, int chan_width,
int cf1, int cf2)
{
- int res;
-
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
@@ -1186,20 +1423,23 @@
return 0;
/* mark radar frequency as invalid */
- res = set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
- cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE);
- if (!res)
+ if (!set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+ cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE))
return 0;
- /* Skip if reported radar event not overlapped our channels */
- res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
- if (!res)
- return 0;
+ if (!hostapd_dfs_is_background_event(iface, freq)) {
+ /* Skip if reported radar event not overlapped our channels */
+ if (!dfs_are_channels_overlapped(iface, freq, chan_width,
+ cf1, cf2))
+ return 0;
+ }
- /* radar detected while operating, switch the channel. */
- res = hostapd_dfs_start_channel_switch(iface);
+ if (hostapd_dfs_background_start_channel_switch(iface, freq)) {
+ /* Radar detected while operating, switch the channel. */
+ return hostapd_dfs_start_channel_switch(iface);
+ }
- return res;
+ return 0;
}
@@ -1219,9 +1459,14 @@
set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
- /* Handle cases where all channels were initially unavailable */
- if (iface->state == HAPD_IFACE_DFS && !iface->cac_started)
+ if (iface->state == HAPD_IFACE_DFS && !iface->cac_started) {
+ /* Handle cases where all channels were initially unavailable */
hostapd_handle_dfs(iface);
+ } else if (dfs_use_radar_background(iface) &&
+ iface->radar_background.channel == -1) {
+ /* Reset radar background chain if disabled */
+ hostpad_dfs_update_background_chain(iface);
+ }
return 0;
}
@@ -1259,17 +1504,24 @@
int ht_enabled, int chan_offset, int chan_width,
int cf1, int cf2)
{
- /* This is called when the driver indicates that an offloaded DFS has
- * started CAC. */
- hostapd_set_state(iface, HAPD_IFACE_DFS);
+ if (hostapd_dfs_is_background_event(iface, freq)) {
+ iface->radar_background.cac_started = 1;
+ } else {
+ /* This is called when the driver indicates that an offloaded
+ * DFS has started CAC. */
+ hostapd_set_state(iface, HAPD_IFACE_DFS);
+ iface->cac_started = 1;
+ }
/* TODO: How to check CAC time for ETSI weather channels? */
iface->dfs_cac_ms = 60000;
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
"freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
- "seg1=%d cac_time=%ds",
+ "seg1=%d cac_time=%ds%s",
freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
- iface->dfs_cac_ms / 1000);
- iface->cac_started = 1;
+ iface->dfs_cac_ms / 1000,
+ hostapd_dfs_is_background_event(iface, freq) ?
+ " (background)" : "");
+
os_get_reltime(&iface->dfs_cac_start);
return 0;
}
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 96a13fb..d4cbed8 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -31,6 +31,7 @@
static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd);
static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd,
struct dpp_authentication *auth);
+static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd);
#ifdef CONFIG_DPP2
static void hostapd_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
void *timeout_ctx);
@@ -346,14 +347,8 @@
#endif /* CONFIG_DPP2 */
-enum hostapd_dpp_pkex_ver {
- PKEX_VER_AUTO,
- PKEX_VER_ONLY_1,
- PKEX_VER_ONLY_2,
-};
-
static int hostapd_dpp_pkex_init(struct hostapd_data *hapd,
- enum hostapd_dpp_pkex_ver ver,
+ enum dpp_pkex_ver ver,
const struct hostapd_ip_addr *ipaddr,
int tcp_port)
{
@@ -1160,6 +1155,21 @@
}
+#ifdef CONFIG_DPP3
+static void hostapd_dpp_build_new_key(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth || !auth->waiting_new_key)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Build config request with a new key");
+ hostapd_dpp_start_gas_client(hapd);
+}
+#endif /* CONFIG_DPP3 */
+
+
static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
enum gas_query_ap_result result,
const struct wpabuf *adv_proto,
@@ -1169,6 +1179,7 @@
const u8 *pos;
struct dpp_authentication *auth = hapd->dpp_auth;
enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED;
+ int res;
if (!auth || !auth->auth_success) {
wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
@@ -1199,7 +1210,16 @@
goto fail;
}
- if (dpp_conf_resp_rx(auth, resp) < 0) {
+ res = dpp_conf_resp_rx(auth, resp);
+#ifdef CONFIG_DPP3
+ if (res == -3) {
+ wpa_printf(MSG_DEBUG, "DPP: New protocol key needed");
+ eloop_register_timeout(0, 0, hostapd_dpp_build_new_key, hapd,
+ NULL);
+ return;
+ }
+#endif /* CONFIG_DPP3 */
+ if (res < 0) {
wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
goto fail;
}
@@ -1986,6 +2006,17 @@
wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR,
MAC2STR(src));
+ if (hapd->dpp_pkex_ver == PKEX_VER_ONLY_1 && v2) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore PKEXv2 Exchange Request when configured to be PKEX v1 only");
+ return;
+ }
+ if (hapd->dpp_pkex_ver == PKEX_VER_ONLY_2 && !v2) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore PKEXv1 Exchange Request when configured to be PKEX v2 only");
+ return;
+ }
+
/* TODO: Support multiple PKEX codes by iterating over all the enabled
* values here */
@@ -2349,6 +2380,13 @@
if (!auth)
return;
+#ifdef CONFIG_DPP3
+ if (auth->waiting_new_key && ok) {
+ wpa_printf(MSG_DEBUG, "DPP: Waiting for a new key");
+ return;
+ }
+#endif /* CONFIG_DPP3 */
+
wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)",
ok);
eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
@@ -2409,6 +2447,11 @@
{
struct dpp_bootstrap_info *own_bi;
const char *pos, *end;
+#ifdef CONFIG_DPP3
+ enum dpp_pkex_ver ver = PKEX_VER_AUTO;
+#else /* CONFIG_DPP3 */
+ enum dpp_pkex_ver ver = PKEX_VER_ONLY_1;
+#endif /* CONFIG_DPP3 */
int tcp_port = DPP_TCP_PORT;
struct hostapd_ip_addr *ipaddr = NULL;
#ifdef CONFIG_DPP2
@@ -2474,27 +2517,22 @@
if (!hapd->dpp_pkex_code)
return -1;
+ pos = os_strstr(cmd, " ver=");
+ if (pos) {
+ int v;
+
+ pos += 5;
+ v = atoi(pos);
+ if (v == 1)
+ ver = PKEX_VER_ONLY_1;
+ else if (v == 2)
+ ver = PKEX_VER_ONLY_2;
+ else
+ return -1;
+ }
+ hapd->dpp_pkex_ver = ver;
+
if (os_strstr(cmd, " init=1")) {
-#ifdef CONFIG_DPP3
- enum hostapd_dpp_pkex_ver ver = PKEX_VER_AUTO;
-#else /* CONFIG_DPP3 */
- enum hostapd_dpp_pkex_ver ver = PKEX_VER_ONLY_1;
-#endif /* CONFIG_DPP3 */
-
- pos = os_strstr(cmd, " ver=");
- if (pos) {
- int v;
-
- pos += 5;
- v = atoi(pos);
- if (v == 1)
- ver = PKEX_VER_ONLY_1;
- else if (v == 2)
- ver = PKEX_VER_ONLY_2;
- else
- return -1;
- }
-
if (hostapd_dpp_pkex_init(hapd, ver, ipaddr, tcp_port) < 0)
return -1;
} else {
@@ -2646,6 +2684,9 @@
if (hapd->iface->interfaces)
dpp_controller_stop_for_ctx(hapd->iface->interfaces->dpp, hapd);
#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+ eloop_cancel_timeout(hostapd_dpp_build_new_key, hapd, NULL);
+#endif /* CONFIG_DPP3 */
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
hostapd_dpp_pkex_remove(hapd, "*");
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index f353a0e..643a273 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -261,12 +261,7 @@
}
#endif /* NEED_AP_MLME */
-#ifdef CONFIG_INTERWORKING
- if (elems.ext_capab && elems.ext_capab_len > 4) {
- if (elems.ext_capab[4] & 0x01)
- sta->qos_map_enabled = 1;
- }
-#endif /* CONFIG_INTERWORKING */
+ check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len);
#ifdef CONFIG_HS20
wpabuf_free(sta->hs20_ie);
@@ -870,10 +865,11 @@
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
- "driver %s channel switch: freq=%d, ht=%d, vht_ch=0x%x, he_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+ "driver %s channel switch: freq=%d, ht=%d, vht_ch=0x%x, he_ch=0x%x, eht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
finished ? "had" : "starting",
freq, ht, hapd->iconf->ch_switch_vht_config,
- hapd->iconf->ch_switch_he_config, offset,
+ hapd->iconf->ch_switch_he_config,
+ hapd->iconf->ch_switch_eht_config, offset,
width, channel_width_to_string(width), cf1, cf2);
if (!hapd->iface->current_mode) {
@@ -953,9 +949,23 @@
else if (hapd->iconf->ch_switch_he_config &
CH_SWITCH_HE_DISABLED)
hapd->iconf->ieee80211ax = 0;
+#ifdef CONFIG_IEEE80211BE
+ } else if (hapd->iconf->ch_switch_eht_config) {
+ /* CHAN_SWITCH EHT config */
+ if (hapd->iconf->ch_switch_eht_config &
+ CH_SWITCH_EHT_ENABLED) {
+ hapd->iconf->ieee80211be = 1;
+ hapd->iconf->ieee80211ax = 1;
+ if (!is_6ghz_freq(hapd->iface->freq))
+ hapd->iconf->ieee80211ac = 1;
+ } else if (hapd->iconf->ch_switch_eht_config &
+ CH_SWITCH_EHT_DISABLED)
+ hapd->iconf->ieee80211be = 0;
+#endif /* CONFIG_IEEE80211BE */
}
hapd->iconf->ch_switch_vht_config = 0;
hapd->iconf->ch_switch_he_config = 0;
+ hapd->iconf->ch_switch_eht_config = 0;
if (width == CHAN_WIDTH_40 || width == CHAN_WIDTH_80 ||
width == CHAN_WIDTH_80P80 || width == CHAN_WIDTH_160)
@@ -1011,7 +1021,9 @@
hostapd_neighbor_set_own_report(hapd->iface->bss[i]);
#ifdef CONFIG_OCV
- if (hapd->conf->ocv) {
+ if (hapd->conf->ocv &&
+ !(hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_SA_QUERY_OFFLOAD_AP)) {
struct sta_info *sta;
bool check_sa_query = false;
@@ -2084,6 +2096,32 @@
data->wds_sta_interface.ifname,
data->wds_sta_interface.sta_addr);
break;
+#ifdef CONFIG_IEEE80211AX
+ case EVENT_BSS_COLOR_COLLISION:
+ /* The BSS color is shared amongst all BBSs on a specific phy.
+ * Therefore we always start the color change on the primary
+ * BSS. */
+ wpa_printf(MSG_DEBUG, "BSS color collision on %s",
+ hapd->conf->iface);
+ hostapd_switch_color(hapd->iface->bss[0],
+ data->bss_color_collision.bitmap);
+ break;
+ case EVENT_CCA_STARTED_NOTIFY:
+ wpa_printf(MSG_DEBUG, "CCA started on on %s",
+ hapd->conf->iface);
+ break;
+ case EVENT_CCA_ABORTED_NOTIFY:
+ wpa_printf(MSG_DEBUG, "CCA aborted on on %s",
+ hapd->conf->iface);
+ hostapd_cleanup_cca_params(hapd);
+ break;
+ case EVENT_CCA_NOTIFY:
+ wpa_printf(MSG_DEBUG, "CCA finished on on %s",
+ hapd->conf->iface);
+ hapd->iface->conf->he_op.he_bss_color = hapd->cca_color;
+ hostapd_cleanup_cca_params(hapd);
+ break;
+#endif /* CONFIG_IEEE80211AX */
default:
wpa_printf(MSG_DEBUG, "Unknown event %d", event);
break;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 4b88641..ef53c41 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -66,6 +66,10 @@
static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx);
static void hostapd_interface_setup_failure_handler(void *eloop_ctx,
void *timeout_ctx);
+#ifdef CONFIG_IEEE80211AX
+static void hostapd_switch_color_timeout_handler(void *eloop_data,
+ void *user_ctx);
+#endif /* CONFIG_IEEE80211AX */
int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
@@ -462,6 +466,10 @@
}
eloop_cancel_timeout(auth_sae_process_commit, hapd, NULL);
#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_IEEE80211AX
+ eloop_cancel_timeout(hostapd_switch_color_timeout_handler, hapd, NULL);
+#endif /* CONFIG_IEEE80211AX */
}
@@ -1458,14 +1466,14 @@
}
-static void hostapd_set_acl(struct hostapd_data *hapd)
+int hostapd_set_acl(struct hostapd_data *hapd)
{
struct hostapd_config *conf = hapd->iconf;
- int err;
+ int err = 0;
u8 accept_acl;
if (hapd->iface->drv_max_acl_mac_addrs == 0)
- return;
+ return 0;
if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) {
accept_acl = 1;
@@ -1474,7 +1482,7 @@
accept_acl);
if (err) {
wpa_printf(MSG_DEBUG, "Failed to set accept acl");
- return;
+ return -1;
}
} else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) {
accept_acl = 0;
@@ -1483,9 +1491,10 @@
accept_acl);
if (err) {
wpa_printf(MSG_DEBUG, "Failed to set deny acl");
- return;
+ return -1;
}
}
+ return err;
}
@@ -2067,6 +2076,7 @@
hapd->iconf->ieee80211n,
hapd->iconf->ieee80211ac,
hapd->iconf->ieee80211ax,
+ hapd->iconf->ieee80211be,
hapd->iconf->secondary_channel,
hostapd_get_oper_chwidth(hapd->iconf),
hostapd_get_oper_centr_freq_seg0_idx(
@@ -3452,12 +3462,14 @@
conf->channel, conf->enable_edmg,
conf->edmg_channel, conf->ieee80211n,
conf->ieee80211ac, conf->ieee80211ax,
- conf->secondary_channel,
+ conf->ieee80211be, conf->secondary_channel,
hostapd_get_oper_chwidth(conf),
hostapd_get_oper_centr_freq_seg0_idx(conf),
hostapd_get_oper_centr_freq_seg1_idx(conf),
conf->vht_capab,
mode ? &mode->he_capab[IEEE80211_MODE_AP] :
+ NULL,
+ mode ? &mode->eht_capab[IEEE80211_MODE_AP] :
NULL))
return -1;
@@ -3545,11 +3557,12 @@
&hapd->iface->cs_oper_class,
&chan) == NUM_HOSTAPD_MODES) {
wpa_printf(MSG_DEBUG,
- "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d, he_enabled=%d)",
+ "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d, he_enabled=%d, eht_enabled=%d)",
settings->freq_params.freq,
settings->freq_params.sec_channel_offset,
settings->freq_params.vht_enabled,
- settings->freq_params.he_enabled);
+ settings->freq_params.he_enabled,
+ settings->freq_params.eht_enabled);
return -1;
}
@@ -3606,6 +3619,11 @@
void hostapd_chan_switch_config(struct hostapd_data *hapd,
struct hostapd_freq_params *freq_params)
{
+ if (freq_params->eht_enabled)
+ hapd->iconf->ch_switch_eht_config |= CH_SWITCH_EHT_ENABLED;
+ else
+ hapd->iconf->ch_switch_eht_config |= CH_SWITCH_EHT_DISABLED;
+
if (freq_params->he_enabled)
hapd->iconf->ch_switch_he_config |= CH_SWITCH_HE_ENABLED;
else
@@ -3618,7 +3636,8 @@
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
- "CHAN_SWITCH HE config 0x%x VHT config 0x%x",
+ "CHAN_SWITCH EHT config 0x%x HE config 0x%x VHT config 0x%x",
+ hapd->iconf->ch_switch_eht_config,
hapd->iconf->ch_switch_he_config,
hapd->iconf->ch_switch_vht_config);
}
@@ -3696,6 +3715,7 @@
iface->conf->ieee80211n = freq_params->ht_enabled;
iface->conf->ieee80211ac = freq_params->vht_enabled;
iface->conf->ieee80211ax = freq_params->he_enabled;
+ iface->conf->ieee80211be = freq_params->eht_enabled;
/*
* cs_params must not be cleared earlier because the freq_params
@@ -3706,6 +3726,139 @@
hostapd_enable_iface(iface);
}
+
+#ifdef CONFIG_IEEE80211AX
+
+void hostapd_cleanup_cca_params(struct hostapd_data *hapd)
+{
+ hapd->cca_count = 0;
+ hapd->cca_color = 0;
+ hapd->cca_c_off_beacon = 0;
+ hapd->cca_c_off_proberesp = 0;
+ hapd->cca_in_progress = false;
+}
+
+
+static int hostapd_fill_cca_settings(struct hostapd_data *hapd,
+ struct cca_settings *settings)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ u8 old_color;
+ int ret;
+
+ if (!iface || iface->conf->he_op.he_bss_color_disabled)
+ return -1;
+
+ old_color = iface->conf->he_op.he_bss_color;
+ iface->conf->he_op.he_bss_color = hapd->cca_color;
+ ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
+ if (ret)
+ return ret;
+
+ iface->conf->he_op.he_bss_color = old_color;
+
+ settings->cca_count = hapd->cca_count;
+ settings->cca_color = hapd->cca_color,
+ hapd->cca_in_progress = true;
+
+ ret = hostapd_build_beacon_data(hapd, &settings->beacon_cca);
+ if (ret) {
+ free_beacon_data(&settings->beacon_after);
+ return ret;
+ }
+
+ settings->counter_offset_beacon = hapd->cca_c_off_beacon;
+ settings->counter_offset_presp = hapd->cca_c_off_proberesp;
+
+ return 0;
+}
+
+
+static void hostapd_switch_color_timeout_handler(void *eloop_data,
+ void *user_ctx)
+{
+ struct hostapd_data *hapd = (struct hostapd_data *) eloop_data;
+ os_time_t delta_t;
+ unsigned int b;
+ int i, r;
+
+ /* CCA can be triggered once the handler constantly receives
+ * color collision events to for at least
+ * DOT11BSS_COLOR_COLLISION_AP_PERIOD (50 s by default). */
+ delta_t = hapd->last_color_collision.sec -
+ hapd->first_color_collision.sec;
+ if (delta_t < DOT11BSS_COLOR_COLLISION_AP_PERIOD)
+ return;
+
+ r = os_random() % HE_OPERATION_BSS_COLOR_MAX;
+ for (i = 0; i < HE_OPERATION_BSS_COLOR_MAX; i++) {
+ if (r && !(hapd->color_collision_bitmap & BIT(r)))
+ break;
+
+ r = (r + 1) % HE_OPERATION_BSS_COLOR_MAX;
+ }
+
+ if (i == HE_OPERATION_BSS_COLOR_MAX) {
+ /* There are no free colors so turn BSS coloring off */
+ wpa_printf(MSG_INFO,
+ "No free colors left, turning off BSS coloring");
+ hapd->iface->conf->he_op.he_bss_color_disabled = 1;
+ hapd->iface->conf->he_op.he_bss_color = os_random() % 63 + 1;
+ for (b = 0; b < hapd->iface->num_bss; b++)
+ ieee802_11_set_beacon(hapd->iface->bss[b]);
+ return;
+ }
+
+ for (b = 0; b < hapd->iface->num_bss; b++) {
+ struct hostapd_data *bss = hapd->iface->bss[b];
+ struct cca_settings settings;
+ int ret;
+
+ hostapd_cleanup_cca_params(bss);
+ bss->cca_color = r;
+ bss->cca_count = 10;
+
+ if (hostapd_fill_cca_settings(bss, &settings)) {
+ hostapd_cleanup_cca_params(bss);
+ continue;
+ }
+
+ ret = hostapd_drv_switch_color(bss, &settings);
+ if (ret)
+ hostapd_cleanup_cca_params(bss);
+
+ free_beacon_data(&settings.beacon_cca);
+ free_beacon_data(&settings.beacon_after);
+ }
+}
+
+
+void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap)
+{
+ struct os_reltime now;
+
+ if (hapd->cca_in_progress)
+ return;
+
+ if (os_get_reltime(&now))
+ return;
+
+ hapd->color_collision_bitmap = bitmap;
+ hapd->last_color_collision = now;
+
+ if (eloop_is_timeout_registered(hostapd_switch_color_timeout_handler,
+ hapd, NULL))
+ return;
+
+ hapd->first_color_collision = now;
+ /* 10 s window as margin for persistent color collision reporting */
+ eloop_register_timeout(DOT11BSS_COLOR_COLLISION_AP_PERIOD + 10, 0,
+ hostapd_switch_color_timeout_handler,
+ hapd, NULL);
+}
+
+#endif /* CONFIG_IEEE80211AX */
+
#endif /* NEED_AP_MLME */
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 7f7877b..6b9b65f 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -14,6 +14,7 @@
#endif /* CONFIG_SQLITE */
#include "common/defs.h"
+#include "common/dpp.h"
#include "utils/list.h"
#include "ap_config.h"
#include "drivers/driver.h"
@@ -207,6 +208,7 @@
void *ssl_ctx;
void *eap_sim_db_priv;
+ struct crypto_rsa_key *imsi_privacy_key;
struct radius_server_data *radius_srv;
struct dl_list erp_keys; /* struct eap_server_erp_key */
@@ -294,6 +296,17 @@
unsigned int cs_c_off_ecsa_beacon;
unsigned int cs_c_off_ecsa_proberesp;
+#ifdef CONFIG_IEEE80211AX
+ bool cca_in_progress;
+ u8 cca_count;
+ u8 cca_color;
+ unsigned int cca_c_off_beacon;
+ unsigned int cca_c_off_proberesp;
+ struct os_reltime first_color_collision;
+ struct os_reltime last_color_collision;
+ u64 color_collision_bitmap;
+#endif /* CONFIG_IEEE80211AX */
+
#ifdef CONFIG_P2P
struct p2p_data *p2p;
struct p2p_group *p2p_group;
@@ -388,6 +401,7 @@
struct dpp_bootstrap_info *dpp_pkex_bi;
char *dpp_pkex_code;
char *dpp_pkex_identifier;
+ enum dpp_pkex_ver dpp_pkex_ver;
char *dpp_pkex_auth_cmd;
char *dpp_configurator_params;
struct os_reltime dpp_last_init;
@@ -521,6 +535,21 @@
int *basic_rates;
int freq;
+ /* Background radar configuration */
+ struct {
+ int channel;
+ int secondary_channel;
+ int freq;
+ int centr_freq_seg0_idx;
+ int centr_freq_seg1_idx;
+ /* Main chain is on temporary channel during
+ * CAC detection on radar offchain.
+ */
+ unsigned int temp_ch:1;
+ /* CAC started on radar offchain */
+ unsigned int cac_started:1;
+ } radar_background;
+
u16 hw_flags;
/* Number of associated Non-ERP stations (i.e., stations using 802.11b
@@ -648,6 +677,9 @@
int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
+void hostapd_switch_color(struct hostapd_data *hapd, u64 bitmap);
+void hostapd_cleanup_cca_params(struct hostapd_data *hapd);
+
/* utils.c */
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
int (*cb)(void *ctx, const u8 *sa,
@@ -693,4 +725,6 @@
struct fst_wpa_obj *iface_obj);
#endif /* CONFIG_FST */
+int hostapd_set_acl(struct hostapd_data *hapd);
+
#endif /* HOSTAPD_H */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 6140a49..394e292 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -498,6 +498,7 @@
struct sae_password_entry *pw;
struct sae_pt *pt = NULL;
const struct sae_pk *pk = NULL;
+ struct hostapd_sta_wpa_psk_short *psk = NULL;
for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
if (!is_broadcast_ether_addr(pw->peer_addr) &&
@@ -519,6 +520,15 @@
pt = hapd->conf->ssid.pt;
}
+ if (!password) {
+ for (psk = sta->psk; psk; psk = psk->next) {
+ if (psk->is_passphrase) {
+ password = psk->passphrase;
+ break;
+ }
+ }
+ }
+
if (pw_entry)
*pw_entry = pw;
if (s_pt)
@@ -2315,9 +2325,8 @@
}
-static int
-ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
- int res, struct radius_sta *info)
+int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
+ int res, struct radius_sta *info)
{
u32 session_timeout = info->session_timeout;
u32 acct_interim_interval = info->acct_interim_interval;
@@ -3112,6 +3121,7 @@
int ret, inc_y;
bool derive_keys;
u32 i;
+ bool derive_kdk;
if (!groups)
groups = default_groups;
@@ -3151,10 +3161,14 @@
sta->pasn->akmp = rsn_data.key_mgmt;
sta->pasn->cipher = rsn_data.pairwise_cipher;
- if (hapd->conf->force_kdk_derivation ||
- ((hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF) &&
- ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
- WLAN_RSNX_CAPAB_SECURE_LTF)))
+ derive_kdk = (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF) &&
+ ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
+ WLAN_RSNX_CAPAB_SECURE_LTF);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!derive_kdk)
+ derive_kdk = hapd->conf->force_kdk_derivation;
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (derive_kdk)
sta->pasn->kdk_len = WPA_KDK_MAX_LEN;
else
sta->pasn->kdk_len = 0;
@@ -4123,32 +4137,6 @@
}
-static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
- const u8 *ext_capab_ie, size_t ext_capab_ie_len)
-{
-#ifdef CONFIG_INTERWORKING
- /* check for QoS Map support */
- if (ext_capab_ie_len >= 5) {
- if (ext_capab_ie[4] & 0x01)
- sta->qos_map_enabled = 1;
- }
-#endif /* CONFIG_INTERWORKING */
-
- if (ext_capab_ie_len > 0) {
- sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
- os_free(sta->ext_capability);
- sta->ext_capability = os_malloc(1 + ext_capab_ie_len);
- if (sta->ext_capability) {
- sta->ext_capability[0] = ext_capab_ie_len;
- os_memcpy(sta->ext_capability + 1, ext_capab_ie,
- ext_capab_ie_len);
- }
- }
-
- return WLAN_STATUS_SUCCESS;
-}
-
-
#ifdef CONFIG_OWE
static int owe_group_supported(struct hostapd_data *hapd, u16 group)
@@ -4203,8 +4191,21 @@
else
return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
- crypto_ecdh_deinit(sta->owe_ecdh);
- sta->owe_ecdh = crypto_ecdh_init(group);
+ if (sta->owe_group == group && sta->owe_ecdh) {
+ /* This is a workaround for mac80211 behavior of retransmitting
+ * the Association Request frames multiple times if the link
+ * layer retries (i.e., seq# remains same) fail. The mac80211
+ * initiated retransmission will use a different seq# and as
+ * such, will go through duplicate detection. If we were to
+ * change our DH key for that attempt, there would be two
+ * different DH shared secrets and the STA would likely select
+ * the wrong one. */
+ wpa_printf(MSG_DEBUG,
+ "OWE: Try to reuse own previous DH key since the STA tried to go through OWE association again");
+ } else {
+ crypto_ecdh_deinit(sta->owe_ecdh);
+ sta->owe_ecdh = crypto_ecdh_init(group);
+ }
if (!sta->owe_ecdh)
return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
sta->owe_group = group;
@@ -4553,6 +4554,17 @@
}
}
#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ resp = copy_sta_eht_capab(hapd, sta, IEEE80211_MODE_AP,
+ elems.he_capabilities,
+ elems.he_capabilities_len,
+ elems.eht_capabilities,
+ elems.eht_capabilities_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
+#endif /* CONFIG_IEEE80211BE */
#ifdef CONFIG_P2P
if (elems.p2p) {
@@ -4924,6 +4936,7 @@
struct ieee80211_ht_capabilities ht_cap;
struct ieee80211_vht_capabilities vht_cap;
struct ieee80211_he_capabilities he_cap;
+ struct ieee80211_eht_capabilities eht_cap;
int set = 1;
/*
@@ -4980,6 +4993,11 @@
sta->he_capab_len);
}
#endif /* CONFIG_IEEE80211AX */
+#ifdef CONFIG_IEEE80211BE
+ if (sta->flags & WLAN_STA_EHT)
+ hostapd_get_eht_capab(hapd, sta->eht_capab, &eht_cap,
+ sta->eht_capab_len);
+#endif /* CONFIG_IEEE80211BE */
/*
* Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
@@ -4993,6 +5011,8 @@
sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
sta->flags & WLAN_STA_HE ? &he_cap : NULL,
sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0,
+ sta->flags & WLAN_STA_EHT ? &eht_cap : NULL,
+ sta->flags & WLAN_STA_EHT ? sta->eht_capab_len : 0,
sta->he_6ghz_capab,
sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
sta->vht_opmode, sta->p2p_ie ? 1 : 0,
@@ -5043,6 +5063,13 @@
if (sta && sta->dpp_pfs)
buflen += 5 + sta->dpp_pfs->curve->prime_len;
#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ buflen += hostapd_eid_eht_capab_len(hapd, IEEE80211_MODE_AP);
+ buflen += 3 + sizeof(struct ieee80211_eht_operation);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
buf = os_zalloc(buflen);
if (!buf) {
res = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -5151,6 +5178,7 @@
if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
p = hostapd_eid_he_operation(hapd, p);
+ p = hostapd_eid_cca(hapd, p);
p = hostapd_eid_spatial_reuse(hapd, p);
p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
p = hostapd_eid_he_6ghz_band_cap(hapd, p);
@@ -5188,6 +5216,13 @@
rsnxe_done:
#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_IEEE80211BE
+ if (hapd->iconf->ieee80211be && !hapd->conf->disable_11be) {
+ p = hostapd_eid_eht_capab(hapd, p, IEEE80211_MODE_AP);
+ p = hostapd_eid_eht_operation(hapd, p);
+ }
+#endif /* CONFIG_IEEE80211BE */
+
#ifdef CONFIG_OWE
if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
@@ -6903,6 +6938,38 @@
}
+static u8 * hostapd_add_tpe_info(u8 *eid, u8 tx_pwr_count,
+ enum max_tx_pwr_interpretation tx_pwr_intrpn,
+ u8 tx_pwr_cat, u8 tx_pwr)
+{
+ int i;
+
+ *eid++ = WLAN_EID_TRANSMIT_POWER_ENVELOPE; /* Element ID */
+ *eid++ = 2 + tx_pwr_count; /* Length */
+
+ /*
+ * Transmit Power Information field
+ * bits 0-2 : Maximum Transmit Power Count
+ * bits 3-5 : Maximum Transmit Power Interpretation
+ * bits 6-7 : Maximum Transmit Power Category
+ */
+ *eid++ = tx_pwr_count | (tx_pwr_intrpn << 3) | (tx_pwr_cat << 6);
+
+ /* Maximum Transmit Power field */
+ for (i = 0; i <= tx_pwr_count; i++)
+ *eid++ = tx_pwr;
+
+ return eid;
+}
+
+
+/*
+ * TODO: Extract power limits from channel data after 6G regulatory
+ * support.
+ */
+#define REG_PSD_MAX_TXPOWER_FOR_DEFAULT_CLIENT (-1) /* dBm/MHz */
+#define REG_PSD_MAX_TXPOWER_FOR_SUBORDINATE_CLIENT 5 /* dBm/MHz */
+
u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
{
struct hostapd_iface *iface = hapd->iface;
@@ -6927,6 +6994,43 @@
if (i == mode->num_channels)
return eid;
+#ifdef CONFIG_IEEE80211AX
+ /* IEEE Std 802.11ax-2021, Annex E.2.7 (6 GHz band in the United
+ * States): An AP that is an Indoor Access Point per regulatory rules
+ * shall send at least two Transmit Power Envelope elements in Beacon
+ * and Probe Response frames as follows:
+ * - Maximum Transmit Power Category subfield = Default;
+ * Unit interpretation = Regulatory client EIRP PSD
+ * - Maximum Transmit Power Category subfield = Subordinate Device;
+ * Unit interpretation = Regulatory client EIRP PSD
+ */
+ if (is_6ghz_op_class(iconf->op_class)) {
+ enum max_tx_pwr_interpretation tx_pwr_intrpn;
+
+ /* Same Maximum Transmit Power for all 20 MHz bands */
+ tx_pwr_count = 0;
+ 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;
+ 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) {
+ /* TODO: Extract PSD limits from channel data */
+ 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);
+ }
+
+ return eid;
+ }
+#endif /* CONFIG_IEEE80211AX */
+
switch (hostapd_get_oper_chwidth(iconf)) {
case CHANWIDTH_USE_HT:
if (iconf->secondary_channel == 0) {
@@ -6999,19 +7103,9 @@
else
tx_pwr = max_tx_power;
- *eid++ = WLAN_EID_TRANSMIT_POWER_ENVELOPE;
- *eid++ = 2 + tx_pwr_count;
-
- /*
- * Max Transmit Power count and
- * Max Transmit Power units = 0 (EIRP)
- */
- *eid++ = tx_pwr_count;
-
- for (i = 0; i <= tx_pwr_count; i++)
- *eid++ = tx_pwr;
-
- return eid;
+ return hostapd_add_tpe_info(eid, tx_pwr_count, LOCAL_EIRP,
+ 0 /* Reserved for bands other than 6 GHz */,
+ tx_pwr);
}
@@ -7022,7 +7116,8 @@
if (!hapd->cs_freq_params.channel ||
(!hapd->cs_freq_params.vht_enabled &&
- !hapd->cs_freq_params.he_enabled))
+ !hapd->cs_freq_params.he_enabled &&
+ !hapd->cs_freq_params.eht_enabled))
return eid;
/* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index c59ad5e..fa1f47b 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -78,6 +78,10 @@
const struct ieee80211_he_capabilities *he_cap,
struct ieee80211_he_capabilities *neg_he_cap,
size_t he_capab_len);
+void hostapd_get_eht_capab(struct hostapd_data *hapd,
+ const struct ieee80211_eht_capabilities *src,
+ struct ieee80211_eht_capabilities *dest,
+ size_t len);
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);
@@ -100,6 +104,7 @@
const u8 *he_6ghz_capab);
int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
enum ieee80211_op_mode mode);
+u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid);
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
const u8 *buf, size_t len, int ack);
void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
@@ -194,7 +199,20 @@
void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len);
+u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ext_capab_ie, size_t ext_capab_ie_len);
size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type);
u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type);
+int ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
+ int res, struct radius_sta *info);
+size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
+ enum ieee80211_op_mode opmode);
+u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
+ enum ieee80211_op_mode opmode);
+u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid);
+u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ enum ieee80211_op_mode opmode,
+ const u8 *he_capab, size_t he_capab_len,
+ const u8 *eht_capab, size_t eht_capab_len);
#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 783ee6d..4277d82 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -1,6 +1,6 @@
/*
* hostapd / IEEE 802.11 authentication (ACL)
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -20,6 +20,8 @@
#include "hostapd.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
#include "ieee802_11.h"
#include "ieee802_1x.h"
#include "ieee802_11_auth.h"
@@ -43,6 +45,11 @@
u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
size_t auth_msg_len;
struct hostapd_acl_query_data *next;
+ bool radius_psk;
+ int akm;
+ u8 *anonce;
+ u8 *eapol;
+ size_t eapol_len;
};
@@ -95,9 +102,11 @@
static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
{
- if (query == NULL)
+ if (!query)
return;
os_free(query->auth_msg);
+ os_free(query->anonce);
+ os_free(query->eapol);
os_free(query);
}
@@ -111,7 +120,7 @@
query->radius_id = radius_client_get_id(hapd->radius);
msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
- if (msg == NULL)
+ if (!msg)
return -1;
if (radius_msg_make_authenticator(msg) < 0) {
@@ -153,6 +162,31 @@
goto fail;
}
+ if (query->akm &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE,
+ wpa_akm_to_suite(query->akm))) {
+ wpa_printf(MSG_DEBUG, "Could not add WLAN-AKM-Suite");
+ goto fail;
+ }
+
+ if (query->anonce &&
+ !radius_msg_add_ext_vs(msg, RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5,
+ RADIUS_VENDOR_ID_FREERADIUS,
+ RADIUS_VENDOR_ATTR_FREERADIUS_802_1X_ANONCE,
+ query->anonce, WPA_NONCE_LEN)) {
+ wpa_printf(MSG_DEBUG, "Could not add FreeRADIUS-802.1X-Anonce");
+ goto fail;
+ }
+
+ if (query->eapol &&
+ !radius_msg_add_ext_vs(msg, RADIUS_ATTR_EXT_VENDOR_SPECIFIC_5,
+ RADIUS_VENDOR_ID_FREERADIUS,
+ RADIUS_VENDOR_ATTR_FREERADIUS_802_1X_EAPOL_KEY_MSG,
+ query->eapol, query->eapol_len)) {
+ wpa_printf(MSG_DEBUG, "Could not add FreeRADIUS-802.1X-EAPoL-Key-Msg");
+ goto fail;
+ }
+
if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
goto fail;
return 0;
@@ -260,23 +294,23 @@
/* No entry in the cache - query external RADIUS server */
query = os_zalloc(sizeof(*query));
- if (query == NULL) {
+ if (!query) {
wpa_printf(MSG_ERROR, "malloc for query data failed");
return HOSTAPD_ACL_REJECT;
}
os_get_reltime(&query->timestamp);
os_memcpy(query->addr, addr, ETH_ALEN);
if (hostapd_radius_acl_query(hapd, addr, query)) {
- wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
- "for ACL query.");
+ wpa_printf(MSG_DEBUG,
+ "Failed to send Access-Request for ACL query.");
hostapd_acl_query_free(query);
return HOSTAPD_ACL_REJECT;
}
query->auth_msg = os_memdup(msg, len);
- if (query->auth_msg == NULL) {
- wpa_printf(MSG_ERROR, "Failed to allocate memory for "
- "auth frame.");
+ if (!query->auth_msg) {
+ wpa_printf(MSG_ERROR,
+ "Failed to allocate memory for auth frame.");
hostapd_acl_query_free(query);
return HOSTAPD_ACL_REJECT;
}
@@ -392,7 +426,7 @@
* Passphrase is NULL iff there is no i-th Tunnel-Password
* attribute in msg.
*/
- if (passphrase == NULL)
+ if (!passphrase)
break;
/*
@@ -464,28 +498,30 @@
prev = query;
query = query->next;
}
- if (query == NULL)
+ if (!query)
return RADIUS_RX_UNKNOWN;
- wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS "
- "message (id=%d)", query->radius_id);
+ wpa_printf(MSG_DEBUG,
+ "Found matching Access-Request for RADIUS message (id=%d)",
+ query->radius_id);
if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
- wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have "
- "correct authenticator - dropped\n");
+ wpa_printf(MSG_INFO,
+ "Incoming RADIUS packet did not have correct authenticator - dropped");
return RADIUS_RX_INVALID_AUTHENTICATOR;
}
if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
hdr->code != RADIUS_CODE_ACCESS_REJECT) {
- wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
- "query", hdr->code);
+ wpa_printf(MSG_DEBUG,
+ "Unknown RADIUS message code %d to ACL query",
+ hdr->code);
return RADIUS_RX_UNKNOWN;
}
/* Insert Accept/Reject info into ACL cache */
cache = os_zalloc(sizeof(*cache));
- if (cache == NULL) {
+ if (!cache) {
wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
goto done;
}
@@ -506,8 +542,9 @@
msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
&info->acct_interim_interval) == 0 &&
info->acct_interim_interval < 60) {
- wpa_printf(MSG_DEBUG, "Ignored too small "
- "Acct-Interim-Interval %d for STA " MACSTR,
+ wpa_printf(MSG_DEBUG,
+ "Ignored too small Acct-Interim-Interval %d for STA "
+ MACSTR,
info->acct_interim_interval,
MAC2STR(query->addr));
info->acct_interim_interval = 0;
@@ -557,20 +594,43 @@
cache->next = hapd->acl_cache;
hapd->acl_cache = cache;
+ if (query->radius_psk) {
+ struct sta_info *sta;
+ bool success = cache->accepted == HOSTAPD_ACL_ACCEPT;
+
+ sta = ap_get_sta(hapd, query->addr);
+ if (!sta || !sta->wpa_sm) {
+ wpa_printf(MSG_DEBUG,
+ "No STA/SM entry found for the RADIUS PSK response");
+ goto done;
+ }
+#ifdef NEED_AP_MLME
+ if (success &&
+ (ieee802_11_set_radius_info(hapd, sta, cache->accepted,
+ info) < 0 ||
+ ap_sta_bind_vlan(hapd, sta) < 0))
+ success = false;
+#endif /* NEED_AP_MLME */
+ wpa_auth_sta_radius_psk_resp(sta->wpa_sm, success);
+ } else {
#ifdef CONFIG_DRIVER_RADIUS_ACL
- hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted,
- info->session_timeout);
+ hostapd_drv_set_radius_acl_auth(hapd, query->addr,
+ cache->accepted,
+ info->session_timeout);
#else /* CONFIG_DRIVER_RADIUS_ACL */
#ifdef NEED_AP_MLME
- /* Re-send original authentication frame for 802.11 processing */
- wpa_printf(MSG_DEBUG, "Re-sending authentication frame after "
- "successful RADIUS ACL query");
- ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL);
+ /* Re-send original authentication frame for 802.11 processing
+ */
+ wpa_printf(MSG_DEBUG,
+ "Re-sending authentication frame after successful RADIUS ACL query");
+ ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len,
+ NULL);
#endif /* NEED_AP_MLME */
#endif /* CONFIG_DRIVER_RADIUS_ACL */
+ }
done:
- if (prev == NULL)
+ if (!prev)
hapd->acl_queries = query->next;
else
prev->next = query->next;
@@ -646,6 +706,40 @@
while (psk) {
struct hostapd_sta_wpa_psk_short *prev = psk;
psk = psk->next;
- os_free(prev);
+ bin_clear_free(prev, sizeof(*prev));
}
}
+
+
+#ifndef CONFIG_NO_RADIUS
+void hostapd_acl_req_radius_psk(struct hostapd_data *hapd, const u8 *addr,
+ int key_mgmt, const u8 *anonce,
+ const u8 *eapol, size_t eapol_len)
+{
+ struct hostapd_acl_query_data *query;
+
+ query = os_zalloc(sizeof(*query));
+ if (!query)
+ return;
+
+ query->radius_psk = true;
+ query->akm = key_mgmt;
+ os_get_reltime(&query->timestamp);
+ os_memcpy(query->addr, addr, ETH_ALEN);
+ if (anonce)
+ query->anonce = os_memdup(anonce, WPA_NONCE_LEN);
+ if (eapol) {
+ query->eapol = os_memdup(eapol, eapol_len);
+ query->eapol_len = eapol_len;
+ }
+ if (hostapd_radius_acl_query(hapd, addr, query)) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to send Access-Request for RADIUS PSK/ACL query");
+ hostapd_acl_query_free(query);
+ return;
+ }
+
+ query->next = hapd->acl_queries;
+ hapd->acl_queries = query;
+}
+#endif /* CONFIG_NO_RADIUS */
diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h
index 9410f55..22ae1a9 100644
--- a/src/ap/ieee802_11_auth.h
+++ b/src/ap/ieee802_11_auth.h
@@ -1,6 +1,6 @@
/*
* hostapd / IEEE 802.11 authentication (ACL)
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -36,5 +36,8 @@
void hostapd_acl_expire(struct hostapd_data *hapd);
void hostapd_copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
struct hostapd_sta_wpa_psk_short *src);
+void hostapd_acl_req_radius_psk(struct hostapd_data *hapd, const u8 *addr,
+ int key_mgmt, const u8 *anonce,
+ const u8 *eapol, size_t eapol_len);
#endif /* IEEE802_11_AUTH_H */
diff --git a/src/ap/ieee802_11_eht.c b/src/ap/ieee802_11_eht.c
new file mode 100644
index 0000000..dbbf9a6
--- /dev/null
+++ b/src/ap/ieee802_11_eht.c
@@ -0,0 +1,353 @@
+/*
+ * hostapd / IEEE 802.11be EHT
+ * Copyright (c) 2021-2022, 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 "hostapd.h"
+#include "sta_info.h"
+#include "ieee802_11.h"
+
+
+static u16 ieee80211_eht_ppet_size(u16 ppe_thres_hdr, const u8 *phy_cap_info)
+{
+ u8 ru;
+ u16 sz = 0;
+
+ if ((phy_cap_info[EHT_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
+ EHT_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
+ return 0;
+
+ ru = (ppe_thres_hdr &
+ EHT_PPE_THRES_RU_INDEX_MASK) >> EHT_PPE_THRES_RU_INDEX_SHIFT;
+ while (ru) {
+ if (ru & 0x1)
+ sz++;
+ ru >>= 1;
+ }
+
+ sz = sz * (1 + ((ppe_thres_hdr & EHT_PPE_THRES_NSS_MASK) >>
+ EHT_PPE_THRES_NSS_SHIFT));
+ sz = (sz * 6) + 9;
+ if (sz % 8)
+ sz += 8;
+ sz /= 8;
+
+ return sz;
+}
+
+
+static u8 ieee80211_eht_mcs_set_size(const u8 *he_phy_cap,
+ const u8 *eht_phy_cap)
+{
+ u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
+
+ if ((he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+ (HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
+ HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
+ HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
+ HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) == 0)
+ return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
+
+ if (he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+ (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
+ HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G))
+ sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
+
+ if (eht_phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
+ EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)
+ sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
+
+ return sz;
+}
+
+
+size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
+ enum ieee80211_op_mode opmode)
+{
+ struct hostapd_hw_modes *mode;
+ struct eht_capabilities *eht_cap;
+ size_t len = 3 + 2 + EHT_PHY_CAPAB_LEN;
+
+ mode = hapd->iface->current_mode;
+ if (!mode)
+ return 0;
+
+ eht_cap = &mode->eht_capab[opmode];
+ if (!eht_cap->eht_supported)
+ return 0;
+
+ len += ieee80211_eht_mcs_set_size(mode->he_capab[opmode].phy_cap,
+ eht_cap->phy_cap);
+ len += ieee80211_eht_ppet_size(WPA_GET_LE16(&eht_cap->ppet[0]),
+ eht_cap->phy_cap);
+
+ return len;
+}
+
+
+u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
+ enum ieee80211_op_mode opmode)
+{
+ struct hostapd_hw_modes *mode;
+ struct eht_capabilities *eht_cap;
+ struct ieee80211_eht_capabilities *cap;
+ size_t mcs_nss_len, ppe_thresh_len;
+ u8 *pos = eid, *length_pos;
+
+ mode = hapd->iface->current_mode;
+ if (!mode)
+ return eid;
+
+ eht_cap = &mode->eht_capab[opmode];
+ if (!eht_cap->eht_supported)
+ return eid;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ length_pos = pos++;
+ *pos++ = WLAN_EID_EXT_EHT_CAPABILITIES;
+
+ cap = (struct ieee80211_eht_capabilities *) pos;
+ os_memset(cap, 0, sizeof(*cap));
+ cap->mac_cap = host_to_le16(eht_cap->mac_cap);
+ os_memcpy(cap->phy_cap, eht_cap->phy_cap, EHT_PHY_CAPAB_LEN);
+
+ if (!is_6ghz_op_class(hapd->iconf->op_class))
+ cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &=
+ ~EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK;
+ if (!hapd->iface->conf->eht_phy_capab.su_beamformer)
+ cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMER_IDX] &=
+ ~EHT_PHYCAP_SU_BEAMFORMER;
+
+ if (!hapd->iface->conf->eht_phy_capab.su_beamformee)
+ cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMEE_IDX] &=
+ ~EHT_PHYCAP_SU_BEAMFORMEE;
+
+ if (!hapd->iface->conf->eht_phy_capab.mu_beamformer)
+ cap->phy_cap[EHT_PHYCAP_MU_BEAMFORMER_IDX] &=
+ ~EHT_PHYCAP_MU_BEAMFORMER_MASK;
+
+ pos = cap->optional;
+
+ mcs_nss_len = ieee80211_eht_mcs_set_size(mode->he_capab[opmode].phy_cap,
+ eht_cap->phy_cap);
+ if (mcs_nss_len) {
+ os_memcpy(pos, eht_cap->mcs, mcs_nss_len);
+ pos += mcs_nss_len;
+ }
+
+ ppe_thresh_len = ieee80211_eht_ppet_size(
+ WPA_GET_LE16(&eht_cap->ppet[0]),
+ eht_cap->phy_cap);
+ if (ppe_thresh_len) {
+ os_memcpy(pos, eht_cap->ppet, ppe_thresh_len);
+ pos += ppe_thresh_len;
+ }
+
+ *length_pos = pos - (eid + 2);
+ return pos;
+}
+
+
+u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+ struct hostapd_config *conf = hapd->iconf;
+ struct ieee80211_eht_operation *oper;
+ u8 *pos = eid, chwidth, seg0 = 0, seg1 = 0;
+
+ if (!hapd->iface->current_mode)
+ return eid;
+
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 5;
+ *pos++ = WLAN_EID_EXT_EHT_OPERATION;
+
+ oper = (struct ieee80211_eht_operation *) pos;
+ oper->oper_params = EHT_OPER_INFO_PRESENT;
+
+ if (is_6ghz_op_class(conf->op_class))
+ chwidth = op_class_to_ch_width(conf->op_class);
+ else
+ chwidth = conf->eht_oper_chwidth;
+
+ seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
+
+ switch (chwidth) {
+#if 0 /* FIX: Need to clean up CHANWIDTH_* use for protocol vs. internal
+ * needs to be able to define this. */
+ case CHANWIDTH_320MHZ:
+ oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_320MHZ;
+ seg1 = seg0;
+ if (hapd->iconf->channel < seg0)
+ seg0 -= 16;
+ else
+ seg0 += 16;
+ break;
+#endif
+ case CHANWIDTH_160MHZ:
+ oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_160MHZ;
+ seg1 = seg0;
+ if (hapd->iconf->channel < seg0)
+ seg0 -= 8;
+ else
+ seg0 += 8;
+ break;
+ case CHANWIDTH_80MHZ:
+ oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_80MHZ;
+ break;
+ case CHANWIDTH_USE_HT:
+ if (seg0)
+ oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_40MHZ;
+ break;
+ default:
+ return eid;
+ }
+
+ oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
+ oper->oper_info.ccfs1 = seg1;
+
+ return pos + 4;
+}
+
+
+static bool check_valid_eht_mcs_nss(struct hostapd_data *hapd, const u8 *ap_mcs,
+ const u8 *sta_mcs, u8 mcs_count, u8 map_len)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < mcs_count; i++) {
+ ap_mcs += i * 3;
+ sta_mcs += i * 3;
+
+ for (j = 0; j < map_len; j++) {
+ if (((ap_mcs[j] >> 4) & 0xFF) == 0)
+ continue;
+
+ if ((sta_mcs[j] & 0xFF) == 0)
+ continue;
+
+ return true;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "No matching EHT MCS found between AP TX and STA RX");
+ return false;
+}
+
+
+static bool check_valid_eht_mcs(struct hostapd_data *hapd,
+ const u8 *sta_eht_capab,
+ enum ieee80211_op_mode opmode)
+{
+ struct hostapd_hw_modes *mode;
+ const struct ieee80211_eht_capabilities *capab;
+ const u8 *ap_mcs, *sta_mcs;
+ u8 mcs_count = 1;
+
+ mode = hapd->iface->current_mode;
+ if (!mode)
+ return true;
+
+ ap_mcs = mode->eht_capab[opmode].mcs;
+ capab = (const struct ieee80211_eht_capabilities *) sta_eht_capab;
+ sta_mcs = capab->optional;
+
+ if (ieee80211_eht_mcs_set_size(mode->he_capab[opmode].phy_cap,
+ mode->eht_capab[opmode].phy_cap) ==
+ EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY)
+ return check_valid_eht_mcs_nss(
+ hapd, ap_mcs, sta_mcs, 1,
+ EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY);
+
+ switch (hapd->iface->conf->eht_oper_chwidth) {
+ /* TODO: CHANWIDTH_320MHZ */
+ case CHANWIDTH_80P80MHZ:
+ case CHANWIDTH_160MHZ:
+ mcs_count = 2;
+ break;
+ }
+
+ return check_valid_eht_mcs_nss(hapd, ap_mcs, sta_mcs, mcs_count,
+ EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS);
+}
+
+
+static bool ieee80211_invalid_eht_cap_size(const u8 *he_cap, const u8 *eht_cap,
+ size_t len)
+{
+ const struct ieee80211_he_capabilities *he_capab;
+ struct ieee80211_eht_capabilities *cap;
+ const u8 *he_phy_cap;
+ size_t cap_len;
+ u16 ppe_thres_hdr;
+
+ he_capab = (const struct ieee80211_he_capabilities *) he_cap;
+ he_phy_cap = he_capab->he_phy_capab_info;
+ cap = (struct ieee80211_eht_capabilities *) eht_cap;
+ cap_len = sizeof(*cap) - sizeof(cap->optional);
+ if (len < cap_len)
+ return true;
+
+ cap_len += ieee80211_eht_mcs_set_size(he_phy_cap, cap->phy_cap);
+ if (len < cap_len)
+ return true;
+
+ ppe_thres_hdr = len > cap_len + 1 ?
+ WPA_GET_LE16(&eht_cap[cap_len]) : 0x01ff;
+ cap_len += ieee80211_eht_ppet_size(ppe_thres_hdr, cap->phy_cap);
+
+ return len < cap_len;
+}
+
+
+u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ enum ieee80211_op_mode opmode,
+ const u8 *he_capab, size_t he_capab_len,
+ const u8 *eht_capab, size_t eht_capab_len)
+{
+ if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be ||
+ !he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
+ !eht_capab ||
+ ieee80211_invalid_eht_cap_size(he_capab, eht_capab,
+ eht_capab_len) ||
+ !check_valid_eht_mcs(hapd, eht_capab, opmode)) {
+ sta->flags &= ~WLAN_STA_EHT;
+ os_free(sta->eht_capab);
+ sta->eht_capab = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ os_free(sta->eht_capab);
+ sta->eht_capab = os_memdup(eht_capab, eht_capab_len);
+ if (!sta->eht_capab) {
+ sta->eht_capab_len = 0;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_EHT;
+ sta->eht_capab_len = eht_capab_len;
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+void hostapd_get_eht_capab(struct hostapd_data *hapd,
+ const struct ieee80211_eht_capabilities *src,
+ struct ieee80211_eht_capabilities *dest,
+ size_t len)
+{
+ if (!src || !dest)
+ return;
+
+ if (len > sizeof(*dest))
+ len = sizeof(*dest);
+ /* TODO: mask out unsupported features */
+
+ os_memset(dest, 0, sizeof(*dest));
+ os_memcpy(dest, src, len);
+}
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 6cd6c90..1e74c58 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -29,17 +29,19 @@
ru = (ppe_thres_hdr >> HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT) &
HE_PPE_THRES_RU_INDEX_BITMASK_MASK;
+ /* Count the number of 1 bits in RU Index Bitmask */
while (ru) {
if (ru & 0x1)
sz++;
ru >>= 1;
}
+ /* fixed header of 3 (NSTS) + 4 (RU Index Bitmask) = 7 bits */
+ /* 6 * (NSTS + 1) bits for bit 1 in RU Index Bitmask */
sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK);
sz = (sz * 6) + 7;
- if (sz % 8)
- sz += 8;
- sz /= 8;
+ /* PPE Pad to count the number of needed full octets */
+ sz = (sz + 7) / 8;
return sz;
}
@@ -64,6 +66,7 @@
{
struct ieee80211_he_capabilities *cap;
size_t cap_len;
+ u8 ppe_thres_hdr;
cap = (struct ieee80211_he_capabilities *) buf;
cap_len = sizeof(*cap) - sizeof(cap->optional);
@@ -74,9 +77,11 @@
if (len < cap_len)
return 1;
- cap_len += ieee80211_he_ppet_size(buf[cap_len], cap->he_phy_capab_info);
+ ppe_thres_hdr = len > cap_len ? buf[cap_len] : 0xff;
+ cap_len += ieee80211_he_ppet_size(ppe_thres_hdr,
+ cap->he_phy_capab_info);
- return len != cap_len;
+ return len < cap_len;
}
@@ -195,7 +200,8 @@
if (hapd->iface->conf->he_op.he_er_su_disable)
params |= HE_OPERATION_ER_SU_DISABLE;
- if (hapd->iface->conf->he_op.he_bss_color_disabled)
+ if (hapd->iface->conf->he_op.he_bss_color_disabled ||
+ hapd->cca_in_progress)
params |= HE_OPERATION_BSS_COLOR_DISABLED;
if (hapd->iface->conf->he_op.he_bss_color_partial)
params |= HE_OPERATION_BSS_COLOR_PARTIAL;
@@ -213,6 +219,7 @@
if (is_6ghz_op_class(hapd->iconf->op_class)) {
u8 seg0 = hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf);
u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
+ u8 control;
if (!seg0)
seg0 = hapd->iconf->channel;
@@ -220,16 +227,28 @@
params |= HE_OPERATION_6GHZ_OPER_INFO;
/* 6 GHz Operation Information field
- * IEEE P802.11ax/D8.0, 9.4.2.249 HE Operation element,
+ * IEEE Std 802.11ax-2021, 9.4.2.249 HE Operation element,
* Figure 9-788k
*/
*pos++ = hapd->iconf->channel; /* Primary Channel */
- /* Control: Channel Width */
+ /* Control:
+ * bits 0-1: Channel Width
+ * bit 2: Duplicate Beacon
+ * bits 3-5: Regulatory Info
+ */
+ /* Channel Width */
if (seg1)
- *pos++ = 3;
+ control = 3;
else
- *pos++ = center_idx_to_bw_6ghz(seg0);
+ 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;
+ *pos++ = control;
/* Channel Center Freq Seg0/Seg1 */
if (hapd->iconf->he_oper_chwidth == 2) {
@@ -520,3 +539,19 @@
return !!(mac_cap[HE_MAC_CAPAB_0] & HE_MACCAP_TWT_RESPONDER) &&
hapd->iface->conf->he_op.he_twt_responder;
}
+
+
+u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid)
+{
+ if (!hapd->cca_in_progress)
+ return eid;
+
+ /* BSS Color Change Announcement element */
+ *eid++ = WLAN_EID_EXTENSION;
+ *eid++ = 3;
+ *eid++ = WLAN_EID_EXT_COLOR_CHANGE_ANNOUNCEMENT;
+ *eid++ = hapd->cca_count; /* Color Switch Countdown */
+ *eid++ = hapd->cca_color; /* New BSS Color Information */
+
+ return eid;
+}
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 4bff9e5..6154895 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -1093,3 +1093,29 @@
return pos;
}
+
+
+u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ext_capab_ie, size_t ext_capab_ie_len)
+{
+#ifdef CONFIG_INTERWORKING
+ /* check for QoS Map support */
+ if (ext_capab_ie_len >= 5) {
+ if (ext_capab_ie[4] & 0x01)
+ sta->qos_map_enabled = 1;
+ }
+#endif /* CONFIG_INTERWORKING */
+
+ if (ext_capab_ie_len > 0) {
+ sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
+ os_free(sta->ext_capability);
+ sta->ext_capability = os_malloc(1 + ext_capab_ie_len);
+ if (sta->ext_capability) {
+ sta->ext_capability[0] = ext_capab_ie_len;
+ os_memcpy(sta->ext_capability + 1, ext_capab_ie,
+ ext_capab_ie_len);
+ }
+ }
+
+ return WLAN_STATUS_SUCCESS;
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 753c883..fb5e920 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -2448,6 +2448,9 @@
conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len;
conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start;
conf.erp_domain = hapd->conf->erp_domain;
+#ifdef CONFIG_TESTING_OPTIONS
+ conf.eap_skip_prot_success = hapd->conf->eap_skip_prot_success;
+#endif /* CONFIG_TESTING_OPTIONS */
os_memset(&cb, 0, sizeof(cb));
cb.eapol_send = ieee802_1x_eapol_send;
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index 229edd2..e37324f 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -225,6 +225,7 @@
int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
int he = hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax;
+ bool eht = he && hapd->iconf->ieee80211be && !hapd->conf->disable_11be;
struct wpa_ssid_value ssid;
u8 channel, op_class;
u8 center_freq1_idx = 0, center_freq2_idx = 0;
@@ -260,10 +261,12 @@
/* VHT bit added in IEEE P802.11-REVmc/D4.3 */
if (vht)
bssid_info |= NEI_REP_BSSID_INFO_VHT;
- if (he)
- bssid_info |= NEI_REP_BSSID_INFO_HE;
}
+ if (he)
+ bssid_info |= NEI_REP_BSSID_INFO_HE;
+ if (eht)
+ bssid_info |= NEI_REP_BSSID_INFO_EHT;
/* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index ccd1ed9..c541926 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -358,6 +358,7 @@
os_free(sta->vht_operation);
os_free(sta->he_capab);
os_free(sta->he_6ghz_capab);
+ os_free(sta->eht_capab);
hostapd_free_psk_list(sta->psk);
os_free(sta->identity);
os_free(sta->radius_cui);
@@ -410,6 +411,7 @@
#ifdef CONFIG_TESTING_OPTIONS
os_free(sta->sae_postponed_commit);
+ forced_memzero(sta->last_tk, WPA_TK_MAX_LEN);
#endif /* CONFIG_TESTING_OPTIONS */
os_free(sta);
@@ -1251,8 +1253,6 @@
for (psk = ssid->wpa_psk; psk; psk = psk->next)
if (os_memcmp(pmk, psk->psk, PMK_LEN) == 0)
break;
- if (!psk)
- return NULL;
if (!psk || !psk->keyid[0])
return NULL;
@@ -1461,7 +1461,7 @@
buf[0] = '\0';
res = os_snprintf(buf, buflen,
- "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
(flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
(flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
(flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
@@ -1481,6 +1481,7 @@
(flags & WLAN_STA_HT ? "[HT]" : ""),
(flags & WLAN_STA_VHT ? "[VHT]" : ""),
(flags & WLAN_STA_HE ? "[HE]" : ""),
+ (flags & WLAN_STA_EHT ? "[EHT]" : ""),
(flags & WLAN_STA_6GHZ ? "[6GHZ]" : ""),
(flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
(flags & WLAN_STA_WNM_SLEEP_MODE ?
@@ -1553,7 +1554,7 @@
if (hostapd_sta_add(hapd, sta->addr, 0, 0,
sta->supported_rates,
sta->supported_rates_len,
- 0, NULL, NULL, NULL, 0, NULL,
+ 0, NULL, NULL, NULL, 0, NULL, 0, NULL,
sta->flags, 0, 0, 0, 0)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 27e72f9..af8f171 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -42,6 +42,7 @@
#define WLAN_STA_HE BIT(24)
#define WLAN_STA_6GHZ BIT(25)
#define WLAN_STA_PENDING_PASN_FILS_ERP BIT(26)
+#define WLAN_STA_EHT BIT(27)
#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
@@ -213,6 +214,8 @@
struct ieee80211_he_capabilities *he_capab;
size_t he_capab_len;
struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
+ struct ieee80211_eht_capabilities *eht_capab;
+ size_t eht_capab_len;
int sa_query_count; /* number of pending SA Query requests;
* 0 = no SA Query in progress */
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 0042ed6..23a352c 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -409,6 +409,8 @@
u8 dialog_token, reason;
const u8 *pos, *end;
int enabled = hapd->conf->bss_transition;
+ char *hex = NULL;
+ size_t hex_len;
#ifdef CONFIG_MBO
if (hapd->conf->mbo_enabled)
@@ -441,6 +443,17 @@
wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
pos, end - pos);
+ hex_len = 2 * (end - pos) + 1;
+ if (hex_len > 1) {
+ hex = os_malloc(hex_len);
+ if (hex)
+ wpa_snprintf_hex(hex, hex_len, pos, end - pos);
+ }
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ BSS_TM_QUERY MACSTR " reason=%u%s%s",
+ MAC2STR(addr), reason, hex ? " neighbor=" : "", hex);
+ os_free(hex);
+
ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
}
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index bb7ee25..ad91883 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1,6 +1,6 @@
/*
* IEEE 802.11 RSN / WPA Authenticator
- * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -607,7 +607,7 @@
while (group) {
prev = group;
group = group->next;
- os_free(prev);
+ bin_clear_free(prev, sizeof(*prev));
}
os_free(wpa_auth);
@@ -1485,6 +1485,12 @@
struct wpa_authenticator *wpa_auth = eloop_ctx;
struct wpa_state_machine *sm = timeout_ctx;
+ if (sm->waiting_radius_psk) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "Ignore EAPOL-Key timeout while waiting for RADIUS PSK");
+ return;
+ }
+
sm->pending_1_of_4_timeout = 0;
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout");
sm->TimeoutEvt = true;
@@ -1646,7 +1652,7 @@
if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
(key_data_len - 8) / 8, buf, key_data)) {
os_free(hdr);
- os_free(buf);
+ bin_clear_free(buf, key_data_len);
return;
}
WPA_PUT_BE16(key_mic + mic_len, key_data_len);
@@ -1667,10 +1673,10 @@
#endif /* CONFIG_NO_RC4 */
} else {
os_free(hdr);
- os_free(buf);
+ bin_clear_free(buf, key_data_len);
return;
}
- os_free(buf);
+ bin_clear_free(buf, key_data_len);
}
if (key_info & WPA_KEY_INFO_MIC) {
@@ -1823,9 +1829,9 @@
case WPA_DEAUTH:
case WPA_DISASSOC:
sm->DeauthenticationRequest = true;
-#ifdef CONFIG_IEEE80211R_AP
os_memset(sm->PMK, 0, sizeof(sm->PMK));
sm->pmk_len = 0;
+#ifdef CONFIG_IEEE80211R_AP
os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
sm->xxkey_len = 0;
os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
@@ -1853,6 +1859,14 @@
break;
}
+ if (sm->ptkstart_without_success > 3) {
+ wpa_printf(MSG_INFO,
+ "WPA: Multiple EAP reauth attempts without 4-way handshake completion, disconnect "
+ MACSTR, MAC2STR(sm->addr));
+ sm->Disconnect = true;
+ break;
+ }
+
if (!sm->use_ext_key_id &&
sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
wpa_printf(MSG_INFO,
@@ -2195,6 +2209,7 @@
sm->PTKRequest = false;
sm->TimeoutEvt = false;
sm->alt_snonce_valid = false;
+ sm->ptkstart_without_success++;
sm->TimeoutCtr++;
if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
@@ -3026,6 +3041,19 @@
break;
}
+ if (!ok && wpa_key_mgmt_wpa_psk_no_sae(sm->wpa_key_mgmt) &&
+ wpa_auth->conf.radius_psk && wpa_auth->cb->request_radius_psk &&
+ !sm->waiting_radius_psk) {
+ wpa_printf(MSG_DEBUG, "No PSK available - ask RADIUS server");
+ wpa_auth->cb->request_radius_psk(wpa_auth->cb_ctx, sm->addr,
+ sm->wpa_key_mgmt,
+ sm->ANonce,
+ sm->last_rx_eapol_key,
+ sm->last_rx_eapol_key_len);
+ sm->waiting_radius_psk = 1;
+ return;
+ }
+
if (!ok) {
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"invalid MIC in msg 2/4 of 4-Way Handshake");
@@ -3279,6 +3307,7 @@
pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
(const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
NULL, 0);
+ forced_memzero(&igtk, sizeof(igtk));
if (!conf->beacon_prot)
return pos;
@@ -3302,6 +3331,7 @@
pos = wpa_add_kde(pos, RSN_KEY_DATA_BIGTK,
(const u8 *) &bigtk, WPA_BIGTK_KDE_PREFIX_LEN + len,
NULL, 0);
+ forced_memzero(&bigtk, sizeof(bigtk));
return pos;
}
@@ -3382,7 +3412,7 @@
SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
{
u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde = NULL, *pos, stub_gtk[32];
- size_t gtk_len, kde_len, wpa_ie_len;
+ size_t gtk_len, kde_len = 0, wpa_ie_len;
struct wpa_group *gsm = sm->group;
u8 *wpa_ie;
int secure, gtkidx, encr = 0;
@@ -3640,7 +3670,7 @@
WPA_KEY_INFO_KEY_TYPE,
_rsc, sm->ANonce, kde, pos - kde, 0, encr);
done:
- os_free(kde);
+ bin_clear_free(kde, kde_len);
os_free(wpa_ie_buf);
os_free(wpa_ie_buf2);
}
@@ -3709,6 +3739,8 @@
#ifdef CONFIG_IEEE80211R_AP
wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr);
#endif /* CONFIG_IEEE80211R_AP */
+
+ sm->ptkstart_without_success = 0;
}
@@ -3783,6 +3815,11 @@
} else if (wpa_auth_uses_sae(sm) && sm->pmksa) {
SM_ENTER(WPA_PTK, PTKSTART);
#endif /* CONFIG_SAE */
+ } else if (wpa_key_mgmt_wpa_psk_no_sae(sm->wpa_key_mgmt) &&
+ wpa_auth->conf.radius_psk) {
+ wpa_printf(MSG_DEBUG,
+ "INITPSK: No PSK yet available for STA - use RADIUS later");
+ SM_ENTER(WPA_PTK, PTKSTART);
} else {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"no PSK configured for the STA");
@@ -3861,7 +3898,7 @@
struct wpa_group *gsm = sm->group;
const u8 *kde;
u8 *kde_buf = NULL, *pos, hdr[2];
- size_t kde_len;
+ size_t kde_len = 0;
u8 *gtk, stub_gtk[32];
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
@@ -3930,7 +3967,7 @@
(!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
rsc, NULL, kde, kde_len, gsm->GN, 1);
- os_free(kde_buf);
+ bin_clear_free(kde_buf, kde_len);
}
@@ -5572,7 +5609,7 @@
WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
WPA_KEY_INFO_KEY_TYPE,
_rsc, sm->ANonce, kde, pos - kde, 0, encr);
- os_free(kde);
+ bin_clear_free(kde, kde_len);
return 0;
}
@@ -5640,7 +5677,7 @@
(!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
rsc, NULL, kde, kde_len, gsm->GN, 1);
- os_free(kde_buf);
+ bin_clear_free(kde_buf, kde_len);
return 0;
}
@@ -5711,3 +5748,28 @@
#endif /* CONFIG_TESTING_OPTIONS */
+
+
+void wpa_auth_sta_radius_psk_resp(struct wpa_state_machine *sm, bool success)
+{
+ if (!sm->waiting_radius_psk) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore RADIUS PSK response for " MACSTR
+ " that did not wait one",
+ MAC2STR(sm->addr));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "RADIUS PSK response for " MACSTR " (%s)",
+ MAC2STR(sm->addr), success ? "success" : "fail");
+ sm->waiting_radius_psk = 0;
+
+ if (success) {
+ /* Try to process the EAPOL-Key msg 2/4 again */
+ sm->EAPOLKeyReceived = true;
+ } else {
+ sm->Disconnect = true;
+ }
+
+ eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL);
+}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 8f0b5a7..348a1de 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -1,6 +1,6 @@
/*
* hostapd - IEEE 802.11i-2004 / WPA Authenticator
- * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -275,6 +275,8 @@
* PTK derivation regardless of advertised capabilities.
*/
bool force_kdk_derivation;
+
+ bool radius_psk;
};
typedef enum {
@@ -322,6 +324,9 @@
void (*store_ptksa)(void *ctx, const u8 *addr, int cipher,
u32 life_time, const struct wpa_ptk *ptk);
void (*clear_ptksa)(void *ctx, const u8 *addr, int cipher);
+ void (*request_radius_psk)(void *ctx, const u8 *addr, int key_mgmt,
+ const u8 *anonce,
+ const u8 *eapol, size_t eapol_len);
#ifdef CONFIG_IEEE80211R_AP
struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
int (*add_sta_ft)(void *ctx, const u8 *sta_addr);
@@ -578,4 +583,6 @@
void wpa_auth_set_enable_eapol_large_timeout(struct wpa_authenticator *wpa_auth,
u8 val);
+void wpa_auth_sta_radius_psk_resp(struct wpa_state_machine *sm, bool success);
+
#endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index fef1104..7a97613 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -2240,6 +2240,7 @@
wpa_printf(MSG_DEBUG,
"FT: GTK subelem encryption failed: kek_len=%d",
(int) kek_len);
+ forced_memzero(keybuf, sizeof(keybuf));
os_free(subelem);
return NULL;
}
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 9e88ea3..9e8dae1 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -1,6 +1,6 @@
/*
* hostapd / WPA authenticator glue code
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2022, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -29,6 +29,7 @@
#include "ap_drv_ops.h"
#include "ap_config.h"
#include "ieee802_11.h"
+#include "ieee802_11_auth.h"
#include "pmksa_cache_auth.h"
#include "wpa_auth.h"
#include "wpa_auth_glue.h"
@@ -216,6 +217,8 @@
wconf->force_kdk_derivation = conf->force_kdk_derivation;
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_PASN */
+
+ wconf->radius_psk = conf->wpa_psk_radius == PSK_RADIUS_DURING_4WAY_HS;
}
@@ -390,10 +393,14 @@
psk = sta->psk->psk;
for (pos = sta->psk; pos; pos = pos->next) {
if (pos->is_passphrase) {
- pbkdf2_sha1(pos->passphrase,
- hapd->conf->ssid.ssid,
- hapd->conf->ssid.ssid_len, 4096,
- pos->psk, PMK_LEN);
+ if (pbkdf2_sha1(pos->passphrase,
+ hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len, 4096,
+ pos->psk, PMK_LEN) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Error in pbkdf2_sha1()");
+ continue;
+ }
pos->is_passphrase = 0;
}
if (pos->psk == prev_psk) {
@@ -1445,6 +1452,23 @@
#endif /* CONFIG_IEEE80211R_AP */
+#ifndef CONFIG_NO_RADIUS
+static void hostapd_request_radius_psk(void *ctx, const u8 *addr, int key_mgmt,
+ const u8 *anonce,
+ const u8 *eapol, size_t eapol_len)
+{
+ struct hostapd_data *hapd = ctx;
+
+ wpa_printf(MSG_DEBUG, "RADIUS PSK request for " MACSTR " key_mgmt=0x%x",
+ MAC2STR(addr), key_mgmt);
+ wpa_hexdump(MSG_DEBUG, "ANonce", anonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "EAPOL", eapol, eapol_len);
+ hostapd_acl_req_radius_psk(hapd, addr, key_mgmt, anonce, eapol,
+ eapol_len);
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
int hostapd_setup_wpa(struct hostapd_data *hapd)
{
struct wpa_auth_config _conf;
@@ -1488,6 +1512,9 @@
.set_session_timeout = hostapd_wpa_auth_set_session_timeout,
.get_session_timeout = hostapd_wpa_auth_get_session_timeout,
#endif /* CONFIG_IEEE80211R_AP */
+#ifndef CONFIG_NO_RADIUS
+ .request_radius_psk = hostapd_request_radius_psk,
+#endif /* CONFIG_NO_RADIUS */
};
const u8 *wpa_ie;
size_t wpa_ie_len;
@@ -1633,4 +1660,10 @@
hapd->l2 = NULL;
hostapd_wpa_unregister_ft_oui(hapd);
#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ forced_memzero(hapd->last_gtk, WPA_GTK_MAX_LEN);
+ forced_memzero(hapd->last_igtk, WPA_IGTK_MAX_LEN);
+ forced_memzero(hapd->last_bigtk, WPA_BIGTK_MAX_LEN);
+#endif /* CONFIG_TESTING_OPTIONS */
}
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index a6dc1a5..17cb5a2 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -89,6 +89,7 @@
unsigned int rx_eapol_key_secure:1;
unsigned int update_snonce:1;
unsigned int alt_snonce_valid:1;
+ unsigned int waiting_radius_psk:1;
#ifdef CONFIG_IEEE80211R_AP
unsigned int ft_completed:1;
unsigned int pmk_r1_name_valid:1;
@@ -96,6 +97,8 @@
unsigned int is_wnmsleep:1;
unsigned int pmkid_set:1;
+ unsigned int ptkstart_without_success;
+
#ifdef CONFIG_OCV
int ocv_enabled;
#endif /* CONFIG_OCV */
diff --git a/src/ap/wpa_auth_kay.c b/src/ap/wpa_auth_kay.c
index 46d94b4..e2c4e10 100644
--- a/src/ap/wpa_auth_kay.c
+++ b/src/ap/wpa_auth_kay.c
@@ -138,7 +138,6 @@
switch (co) {
case CONFIDENTIALITY_OFFSET_30:
return 30;
- break;
case CONFIDENTIALITY_OFFSET_50:
return 50;
default:
@@ -329,7 +328,9 @@
hapd->conf->macsec_replay_protect,
hapd->conf->macsec_replay_window,
hapd->conf->macsec_port,
- hapd->conf->mka_priority, hapd->conf->iface,
+ hapd->conf->mka_priority,
+ hapd->conf->macsec_csindex,
+ hapd->conf->iface,
hapd->own_addr);
/* ieee802_1x_kay_init() frees kay_ctx on failure */
if (!res)
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 4f1c76b..aacfa33 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -1069,10 +1069,11 @@
for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
wpabuf_free(wps->dev.vendor_ext[i]);
wps_device_data_free(&wps->dev);
- os_free(wps->network_key);
+ bin_clear_free(wps->network_key, wps->network_key_len);
hostapd_wps_nfc_clear(wps);
wpabuf_free(wps->dh_pubkey);
wpabuf_free(wps->dh_privkey);
+ forced_memzero(wps->psk, sizeof(wps->psk));
os_free(wps);
}